2 * Copyright 2006,2007,2009 Free Software Foundation, Inc.
4 * This file is part of GNU Radio
6 * GNU Radio is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3, or (at your option)
11 * GNU Radio is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Radio; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street,
19 * Boston, MA 02110-1301, USA.
26 #include <gr_hier_block2_detail.h>
27 #include <gr_io_signature.h>
31 #define GR_HIER_BLOCK2_DETAIL_DEBUG 0
33 gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) :
36 d_fg(gr_make_flowgraph())
38 int min_inputs = owner->input_signature()->min_streams();
39 int max_inputs = owner->input_signature()->max_streams();
40 int min_outputs = owner->output_signature()->min_streams();
41 int max_outputs = owner->output_signature()->max_streams();
43 if (max_inputs == gr_io_signature::IO_INFINITE ||
44 max_outputs == gr_io_signature::IO_INFINITE ||
45 (min_inputs != max_inputs) ||(min_outputs != max_outputs) ) {
46 std::stringstream msg;
47 msg << "Hierarchical blocks do not yet support arbitrary or"
48 << " variable numbers of inputs or outputs (" << d_owner->name() << ")";
49 throw std::runtime_error(msg.str());
52 d_inputs = std::vector<gr_endpoint_vector_t>(max_inputs);
53 d_outputs = gr_endpoint_vector_t(max_outputs);
56 gr_hier_block2_detail::~gr_hier_block2_detail()
58 d_owner = 0; // Don't use delete, we didn't allocate
62 gr_hier_block2_detail::connect(gr_basic_block_sptr block)
64 std::stringstream msg;
67 if (std::find(d_blocks.begin(), d_blocks.end(), block) != d_blocks.end()) {
68 msg << "Block " << block << " already connected.";
69 throw std::invalid_argument(msg.str());
72 // Check if has inputs or outputs
73 if (block->input_signature()->max_streams() != 0 ||
74 block->output_signature()->max_streams() != 0) {
75 msg << "Block " << block << " must not have any input or output ports";
76 throw std::invalid_argument(msg.str());
79 gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block));
81 if (hblock && hblock.get() != d_owner) {
82 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
83 std::cout << "connect: block is hierarchical, setting parent to " << this << std::endl;
84 hblock->d_detail->d_parent_detail = this;
87 d_blocks.push_back(block);
91 gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port,
92 gr_basic_block_sptr dst, int dst_port)
94 std::stringstream msg;
96 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
97 std::cout << "connecting: " << gr_endpoint(src, src_port)
98 << " -> " << gr_endpoint(dst, dst_port) << std::endl;
100 if (src.get() == dst.get())
101 throw std::invalid_argument("connect: src and destination blocks cannot be the same");
103 gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
104 gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
106 if (src_block && src.get() != d_owner) {
107 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
108 std::cout << "connect: src is hierarchical, setting parent to " << this << std::endl;
109 src_block->d_detail->d_parent_detail = this;
112 if (dst_block && dst.get() != d_owner) {
113 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
114 std::cout << "connect: dst is hierarchical, setting parent to " << this << std::endl;
115 dst_block->d_detail->d_parent_detail = this;
118 // Connections to block inputs or outputs
120 if (src.get() == d_owner) {
121 max_port = src->input_signature()->max_streams();
122 if ((max_port != -1 && (src_port >= max_port)) || src_port < 0) {
123 msg << "source port " << src_port << " out of range for " << src;
124 throw std::invalid_argument(msg.str());
127 return connect_input(src_port, dst_port, dst);
130 if (dst.get() == d_owner) {
131 max_port = dst->output_signature()->max_streams();
132 if ((max_port != -1 && (dst_port >= max_port)) || dst_port < 0) {
133 msg << "destination port " << dst_port << " out of range for " << dst;
134 throw std::invalid_argument(msg.str());
137 return connect_output(dst_port, src_port, src);
140 // Internal connections
141 d_fg->connect(src, src_port, dst, dst_port);
143 // TODO: connects to NC
147 gr_hier_block2_detail::disconnect(gr_basic_block_sptr block)
149 // Check on singleton list
150 for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
154 gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block));
155 if (block && block.get() != d_owner) {
156 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
157 std::cout << "disconnect: block is hierarchical, clearing parent" << std::endl;
158 hblock->d_detail->d_parent_detail = 0;
165 // Otherwise find all edges containing block
166 gr_edge_vector_t edges, tmp = d_fg->edges();
167 gr_edge_vector_t::iterator p;
168 for (p = tmp.begin(); p != tmp.end(); p++) {
169 if ((*p).src().block() == block || (*p).dst().block() == block) {
172 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
173 std::cout << "disconnect: block found in edge " << (*p) << std::endl;
177 if (edges.size() == 0) {
178 std::stringstream msg;
179 msg << "cannot disconnect block " << block << ", not found";
180 throw std::invalid_argument(msg.str());
183 for (p = edges.begin(); p != edges.end(); p++) {
184 disconnect((*p).src().block(), (*p).src().port(),
185 (*p).dst().block(), (*p).dst().port());
190 gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port,
191 gr_basic_block_sptr dst, int dst_port)
193 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
194 std::cout << "disconnecting: " << gr_endpoint(src, src_port)
195 << " -> " << gr_endpoint(dst, dst_port) << std::endl;
197 if (src.get() == dst.get())
198 throw std::invalid_argument("disconnect: source and destination blocks cannot be the same");
200 gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
201 gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
203 if (src_block && src.get() != d_owner) {
204 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
205 std::cout << "disconnect: src is hierarchical, clearing parent" << std::endl;
206 src_block->d_detail->d_parent_detail = 0;
209 if (dst_block && dst.get() != d_owner) {
210 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
211 std::cout << "disconnect: dst is hierarchical, clearing parent" << std::endl;
212 dst_block->d_detail->d_parent_detail = 0;
215 if (src.get() == d_owner)
216 return disconnect_input(src_port, dst_port, dst);
218 if (dst.get() == d_owner)
219 return disconnect_output(dst_port, src_port, src);
221 // Internal connections
222 d_fg->disconnect(src, src_port, dst, dst_port);
226 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
228 std::stringstream msg;
230 if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
231 msg << "input port " << my_port << " out of range for " << block;
232 throw std::invalid_argument(msg.str());
235 gr_endpoint_vector_t &endps = d_inputs[my_port];
236 gr_endpoint endp(block, port);
238 gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp);
239 if (p != endps.end()) {
240 msg << "external input port " << my_port << " already wired to " << endp;
241 throw std::invalid_argument(msg.str());
244 endps.push_back(endp);
248 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
250 std::stringstream msg;
252 if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
253 msg << "output port " << my_port << " out of range for " << block;
254 throw std::invalid_argument(msg.str());
257 if (d_outputs[my_port].block()) {
258 msg << "external output port " << my_port << " already connected from "
259 << d_outputs[my_port];
260 throw std::invalid_argument(msg.str());
263 d_outputs[my_port] = gr_endpoint(block, port);
267 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
269 std::stringstream msg;
271 if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
272 msg << "input port number " << my_port << " out of range for " << block;
273 throw std::invalid_argument(msg.str());
276 gr_endpoint_vector_t &endps = d_inputs[my_port];
277 gr_endpoint endp(block, port);
279 gr_endpoint_viter_t p = std::find(endps.begin(), endps.end(), endp);
280 if (p == endps.end()) {
281 msg << "external input port " << my_port << " not connected to " << endp;
282 throw std::invalid_argument(msg.str());
289 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
291 std::stringstream msg;
293 if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
294 msg << "output port number " << my_port << " out of range for " << block;
295 throw std::invalid_argument(msg.str());
298 if (d_outputs[my_port].block() != block) {
299 msg << "block " << block << " not assigned to output "
300 << my_port << ", can't disconnect";
301 throw std::invalid_argument(msg.str());
304 d_outputs[my_port] = gr_endpoint();
308 gr_hier_block2_detail::resolve_port(int port, bool is_input)
310 std::stringstream msg;
312 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
313 std::cout << "Resolving port " << port << " as an "
314 << (is_input ? "input" : "output")
315 << " of " << d_owner->name() << std::endl;
317 gr_endpoint_vector_t result;
320 if (port < 0 || port >= (signed)d_inputs.size()) {
321 msg << "resolve_port: input " << port << " is out of range";
322 throw std::runtime_error(msg.str());
325 if (d_inputs[port].empty()) {
326 msg << "hierarchical block '" << d_owner->name() << "' input " << port
327 << " is not connected internally";
328 throw std::runtime_error(msg.str());
331 gr_endpoint_vector_t &endps = d_inputs[port];
332 gr_endpoint_viter_t p;
333 for (p = endps.begin(); p != endps.end(); p++) {
334 gr_endpoint_vector_t tmp = resolve_endpoint(*p, true);
335 std::copy(tmp.begin(), tmp.end(), back_inserter(result));
339 if (port < 0 || port >= (signed)d_outputs.size()) {
340 msg << "resolve_port: output " << port << " is out of range";
341 throw std::runtime_error(msg.str());
344 if (d_outputs[port] == gr_endpoint()) {
345 msg << "hierarchical block '" << d_owner->name() << "' output " << port
346 << " is not connected internally";
347 throw std::runtime_error(msg.str());
350 result = resolve_endpoint(d_outputs[port], false);
353 if (result.empty()) {
354 msg << "unable to resolve "
355 << (is_input ? "input port " : "output port ")
357 throw std::runtime_error(msg.str());
364 gr_hier_block2_detail::disconnect_all()
373 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
375 std::stringstream msg;
376 gr_endpoint_vector_t result;
378 // Check if endpoint is a leaf node
379 if (cast_to_block_sptr(endp.block())) {
380 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
381 std::cout << "Block " << endp.block() << " is a leaf node, returning." << std::endl;
382 result.push_back(endp);
386 // Check if endpoint is a hierarchical block
387 gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
389 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
390 std::cout << "Resolving endpoint " << endp << " as an "
391 << (is_input ? "input" : "output")
392 << ", recursing" << std::endl;
393 return hier_block2->d_detail->resolve_port(endp.port(), is_input);
396 msg << "unable to resolve" << (is_input ? " input " : " output ")
397 << "endpoint " << endp;
398 throw std::runtime_error(msg.str());
402 gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
404 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
405 std::cout << "Flattening " << d_owner->name() << std::endl;
407 // Add my edges to the flow graph, resolving references to actual endpoints
408 gr_edge_vector_t edges = d_fg->edges();
411 for (p = edges.begin(); p != edges.end(); p++) {
412 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
413 std::cout << "Flattening edge " << (*p) << std::endl;
415 gr_endpoint_vector_t src_endps = resolve_endpoint(p->src(), false);
416 gr_endpoint_vector_t dst_endps = resolve_endpoint(p->dst(), true);
418 gr_endpoint_viter_t s, d;
419 for (s = src_endps.begin(); s != src_endps.end(); s++) {
420 for (d = dst_endps.begin(); d != dst_endps.end(); d++) {
421 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
422 std::cout << (*s) << "->" << (*d) << std::endl;
423 sfg->connect(*s, *d);
428 // Construct unique list of blocks used either in edges, inputs,
429 // outputs, or by themselves. I still hate STL.
430 gr_basic_block_vector_t blocks; // unique list of used blocks
431 gr_basic_block_vector_t tmp = d_fg->calc_used_blocks();
433 // First add the list of singleton blocks
434 std::vector<gr_basic_block_sptr>::const_iterator b; // Because flatten_aux is const
435 for (b = d_blocks.begin(); b != d_blocks.end(); b++)
438 // Now add the list of connected input blocks
439 std::stringstream msg;
440 for (unsigned int i = 0; i < d_inputs.size(); i++) {
441 if (d_inputs[i].size() == 0) {
442 msg << "In hierarchical block " << d_owner->name() << ", input " << i
443 << " is not connected internally";
444 throw std::runtime_error(msg.str());
447 for (unsigned int j = 0; j < d_inputs[i].size(); j++)
448 tmp.push_back(d_inputs[i][j].block());
451 for (unsigned int i = 0; i < d_outputs.size(); i++) {
452 gr_basic_block_sptr blk = d_outputs[i].block();
454 msg << "In hierarchical block " << d_owner->name() << ", output " << i
455 << " is not connected internally";
456 throw std::runtime_error(msg.str());
460 sort(tmp.begin(), tmp.end());
462 std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
463 unique_copy(tmp.begin(), tmp.end(), inserter);
465 // Recurse hierarchical children
466 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
467 gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(*p));
469 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
470 std::cout << "flatten_aux: recursing into hierarchical block " << hier_block2 << std::endl;
471 hier_block2->d_detail->flatten_aux(sfg);
477 gr_hier_block2_detail::lock()
479 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
480 std::cout << "lock: entered in " << this << std::endl;
483 d_parent_detail->lock();
489 gr_hier_block2_detail::unlock()
491 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
492 std::cout << "unlock: entered in " << this << std::endl;
495 d_parent_detail->unlock();