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 = 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);
225 // FIXME: ticket:161 will be implemented here
227 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
229 std::stringstream msg;
231 if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
232 msg << "input port " << my_port << " out of range for " << block;
233 throw std::invalid_argument(msg.str());
236 if (d_inputs[my_port].block()) {
237 msg << "external input port " << my_port << " already wired to "
238 << d_inputs[my_port];
239 throw std::invalid_argument(msg.str());
242 d_inputs[my_port] = gr_endpoint(block, port);
246 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
248 std::stringstream msg;
250 if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
251 msg << "output port " << my_port << " out of range for " << block;
252 throw std::invalid_argument(msg.str());
255 if (d_outputs[my_port].block()) {
256 msg << "external output port " << my_port << " already connected from "
257 << d_outputs[my_port];
258 throw std::invalid_argument(msg.str());
261 d_outputs[my_port] = gr_endpoint(block, port);
265 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
267 std::stringstream msg;
269 if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
270 msg << "input port number " << my_port << " out of range for " << block;
271 throw std::invalid_argument(msg.str());
274 if (d_inputs[my_port].block() != block) {
275 msg << "block " << block << " not assigned to input "
276 << my_port << ", can't disconnect";
277 throw std::invalid_argument(msg.str());
280 d_inputs[my_port] = gr_endpoint();
284 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
286 std::stringstream msg;
288 if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
289 msg << "output port number " << my_port << " out of range for " << block;
290 throw std::invalid_argument(msg.str());
293 if (d_outputs[my_port].block() != block) {
294 msg << "block " << block << " not assigned to output "
295 << my_port << ", can't disconnect";
296 throw std::invalid_argument(msg.str());
299 d_outputs[my_port] = gr_endpoint();
303 gr_hier_block2_detail::resolve_port(int port, bool is_input)
305 std::stringstream msg;
307 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
308 std::cout << "Resolving port " << port << " as an "
309 << (is_input ? "input" : "output")
310 << " of " << d_owner->name() << std::endl;
315 if (port < 0 || port >= (signed)d_inputs.size()) {
316 msg << "resolve_port: input " << port << " is out of range";
317 throw std::runtime_error(msg.str());
320 if (d_inputs[port] == gr_endpoint()) {
321 msg << "hierarchical block '" << d_owner->name() << "' input " << port
322 << " is not connected internally";
323 throw std::runtime_error(msg.str());
326 result = resolve_endpoint(d_inputs[port], true);
329 if (port < 0 || port >= (signed)d_outputs.size()) {
330 msg << "resolve_port: output " << port << " is out of range";
331 throw std::runtime_error(msg.str());
334 if (d_outputs[port] == gr_endpoint()) {
335 msg << "hierarchical block '" << d_owner->name() << "' output " << port
336 << " is not connected internally";
337 throw std::runtime_error(msg.str());
340 result = resolve_endpoint(d_outputs[port], false);
343 if (!result.block()) {
344 msg << "unable to resolve "
345 << (is_input ? "input port " : "output port ")
347 throw std::runtime_error(msg.str());
354 gr_hier_block2_detail::disconnect_all()
363 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
365 std::stringstream msg;
367 // Check if endpoint is a leaf node
368 if (cast_to_block_sptr(endp.block())) {
369 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
370 std::cout << "Block " << endp.block() << " is a leaf node, returning." << std::endl;
374 // Check if endpoint is a hierarchical block
375 gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
377 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
378 std::cout << "Resolving endpoint " << endp << " as an "
379 << (is_input ? "input" : "output")
380 << ", recursing" << std::endl;
381 return hier_block2->d_detail->resolve_port(endp.port(), is_input);
384 msg << "unable to resolve" << (is_input ? " input " : " output ")
385 << "endpoint " << endp;
386 throw std::runtime_error(msg.str());
390 gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
392 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
393 std::cout << "Flattening " << d_owner->name() << std::endl;
395 // Add my edges to the flow graph, resolving references to actual endpoints
396 gr_edge_vector_t edges = d_fg->edges();
398 for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
399 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
400 std::cout << "Flattening edge " << (*p) << std::endl;
402 gr_endpoint src_endp = resolve_endpoint(p->src(), false);
403 gr_endpoint dst_endp = resolve_endpoint(p->dst(), true);
405 if (GR_HIER_BLOCK2_DETAIL_DEBUG) {
406 std::cout << "src_endp = " << src_endp
407 << ", dst_endp = " << dst_endp << std::endl;
410 sfg->connect(src_endp, dst_endp);
413 // Construct unique list of blocks used either in edges, inputs,
414 // outputs, or by themselves. I still hate STL.
415 gr_basic_block_vector_t blocks, tmp = d_fg->calc_used_blocks();
417 std::vector<gr_basic_block_sptr>::const_iterator p; // Because flatten_aux is const
418 for (p = d_blocks.begin(); p != d_blocks.end(); p++)
421 std::vector<gr_endpoint>::const_iterator e; // Because flatten_aux is const
422 for (e = d_inputs.begin(); e != d_inputs.end(); e++)
423 tmp.push_back((*e).block());
424 for (e = d_outputs.begin(); e != d_outputs.end(); e++)
425 tmp.push_back((*e).block());
427 sort(tmp.begin(), tmp.end());
429 std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
430 unique_copy(tmp.begin(), tmp.end(), inserter);
432 // Recurse hierarchical children
433 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
434 gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(*p));
436 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
437 std::cout << "flatten_aux: recursing into hierarchical block " << hier_block2 << std::endl;
438 hier_block2->d_detail->flatten_aux(sfg);
444 gr_hier_block2_detail::lock()
446 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
447 std::cout << "lock: entered in " << this << std::endl;
450 d_parent_detail->lock();
456 gr_hier_block2_detail::unlock()
458 if (GR_HIER_BLOCK2_DETAIL_DEBUG)
459 std::cout << "unlock: entered in " << this << std::endl;
462 d_parent_detail->unlock();