Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_hier_block2_detail.cc
1 /*
2  * Copyright 2006,2007,2009 Free Software Foundation, Inc.
3  * 
4  * This file is part of GNU Radio
5  * 
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)
9  * any later version.
10  * 
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.
15  * 
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.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <gr_hier_block2_detail.h>
27 #include <gr_io_signature.h>
28 #include <stdexcept>
29 #include <sstream>
30
31 #define GR_HIER_BLOCK2_DETAIL_DEBUG 0
32
33 gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) :
34   d_owner(owner), 
35   d_parent_detail(0),
36   d_fg(gr_make_flowgraph())
37 {
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();
42
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());
50   }
51
52   d_inputs = std::vector<gr_endpoint_vector_t>(max_inputs);
53   d_outputs = gr_endpoint_vector_t(max_outputs);
54 }
55
56 gr_hier_block2_detail::~gr_hier_block2_detail()
57 {
58   d_owner = 0; // Don't use delete, we didn't allocate
59 }
60
61 void
62 gr_hier_block2_detail::connect(gr_basic_block_sptr block)
63 {
64   std::stringstream msg;
65
66   // Check if duplicate
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());
70   }
71
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());
77   }
78
79   gr_hier_block2_sptr hblock(cast_to_hier_block2_sptr(block));
80
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;
85   }
86                 
87   d_blocks.push_back(block);
88 }
89
90 void 
91 gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port, 
92                                gr_basic_block_sptr dst, int dst_port)
93 {
94   std::stringstream msg;
95   
96   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
97     std::cout << "connecting: " << gr_endpoint(src, src_port)
98               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
99
100   if (src.get() == dst.get())
101     throw std::invalid_argument("connect: src and destination blocks cannot be the same");
102
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));
105
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;
110   }
111                 
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;
116   }
117
118   // Connections to block inputs or outputs
119   int max_port;
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());
125     }
126
127     return connect_input(src_port, dst_port, dst);
128   }
129
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());
135     }
136
137     return connect_output(dst_port, src_port, src);
138   }
139
140   // Internal connections
141   d_fg->connect(src, src_port, dst, dst_port);
142
143   // TODO: connects to NC
144 }
145
146 void
147 gr_hier_block2_detail::disconnect(gr_basic_block_sptr block)
148 {
149   // Check on singleton list
150   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
151     if (*p == block) {
152       d_blocks.erase(p);
153       
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;
159       }
160     
161       return;
162     }
163   }
164
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) {
170       edges.push_back(*p);
171
172       if (GR_HIER_BLOCK2_DETAIL_DEBUG)
173         std::cout << "disconnect: block found in edge " << (*p) << std::endl;  
174     }
175   }
176
177   if (edges.size() == 0) {
178     std::stringstream msg;
179     msg << "cannot disconnect block " << block << ", not found";
180     throw std::invalid_argument(msg.str());
181   }
182
183   for (p = edges.begin(); p != edges.end(); p++) {
184     disconnect((*p).src().block(), (*p).src().port(),
185                (*p).dst().block(), (*p).dst().port());
186   }
187 }
188
189 void 
190 gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port, 
191                                   gr_basic_block_sptr dst, int dst_port)
192 {
193   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
194     std::cout << "disconnecting: " << gr_endpoint(src, src_port)
195               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
196
197   if (src.get() == dst.get())
198     throw std::invalid_argument("disconnect: source and destination blocks cannot be the same");
199
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));
202
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;
207   }
208                 
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;
213   }
214
215   if (src.get() == d_owner)
216     return disconnect_input(src_port, dst_port, dst);
217
218   if (dst.get() == d_owner)
219     return disconnect_output(dst_port, src_port, src);
220
221   // Internal connections
222   d_fg->disconnect(src, src_port, dst, dst_port);
223 }
224
225 void
226 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
227 {
228   std::stringstream msg;
229
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());
233   }
234
235   gr_endpoint_vector_t &endps = d_inputs[my_port];
236   gr_endpoint endp(block, port);
237
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());
242   }
243   
244   endps.push_back(endp);
245 }
246
247 void
248 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
249 {
250   std::stringstream msg;
251
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());
255   }
256
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());
261   }
262
263   d_outputs[my_port] = gr_endpoint(block, port);
264 }
265
266 void
267 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
268 {
269   std::stringstream msg;
270
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());
274   }
275
276   gr_endpoint_vector_t &endps = d_inputs[my_port];
277   gr_endpoint endp(block, port);
278
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());
283   }
284   
285   endps.erase(p);
286 }
287
288 void
289 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
290 {
291   std::stringstream msg;
292
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());
296   }
297
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());
302   }
303
304   d_outputs[my_port] = gr_endpoint();
305 }
306
307 gr_endpoint_vector_t
308 gr_hier_block2_detail::resolve_port(int port, bool is_input)
309 {
310   std::stringstream msg;
311
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;
316
317   gr_endpoint_vector_t result;
318
319   if (is_input) {
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());
323     }
324
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());
329     }
330
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));
336     }
337   }
338   else {
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());
342     }
343
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());
348     }
349
350     result = resolve_endpoint(d_outputs[port], false);
351   }
352
353   if (result.empty()) {
354     msg << "unable to resolve " 
355         << (is_input ? "input port " : "output port ")
356         << port;
357     throw std::runtime_error(msg.str());
358   }
359
360   return result;
361 }
362
363 void
364 gr_hier_block2_detail::disconnect_all()
365 {
366   d_fg->clear();
367   d_blocks.clear();
368   d_inputs.clear();
369   d_outputs.clear();
370 }
371
372 gr_endpoint_vector_t
373 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
374 {
375   std::stringstream msg;
376   gr_endpoint_vector_t result;
377
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);
383     return result;
384   }
385
386   // Check if endpoint is a hierarchical block
387   gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
388   if (hier_block2) {
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);
394   }
395
396   msg << "unable to resolve" << (is_input ? " input " : " output ")
397       << "endpoint " << endp;
398   throw std::runtime_error(msg.str());
399 }
400
401 void
402 gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
403 {
404   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
405     std::cout << "Flattening " << d_owner->name() << std::endl;
406
407   // Add my edges to the flow graph, resolving references to actual endpoints
408   gr_edge_vector_t edges = d_fg->edges();
409   gr_edge_viter_t p;
410
411   for (p = edges.begin(); p != edges.end(); p++) {
412     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
413       std::cout << "Flattening edge " << (*p) << std::endl;
414
415     gr_endpoint_vector_t src_endps = resolve_endpoint(p->src(), false);
416     gr_endpoint_vector_t dst_endps = resolve_endpoint(p->dst(), true);
417
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);
424       }
425     }
426   }
427
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();
432
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++)
436     tmp.push_back(*b);
437
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());
445     }
446     
447     for (unsigned int j = 0; j < d_inputs[i].size(); j++)
448       tmp.push_back(d_inputs[i][j].block());
449   }
450
451   for (unsigned int i = 0; i < d_outputs.size(); i++) {
452     gr_basic_block_sptr blk = d_outputs[i].block();
453     if (!blk) {
454       msg << "In hierarchical block " << d_owner->name() << ", output " << i
455           << " is not connected internally";
456       throw std::runtime_error(msg.str());
457     }
458     tmp.push_back(blk);
459   }
460   sort(tmp.begin(), tmp.end());
461
462   std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
463   unique_copy(tmp.begin(), tmp.end(), inserter);
464
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));
468     if (hier_block2) {
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);
472     }
473   }
474 }
475
476 void
477 gr_hier_block2_detail::lock()
478 {
479   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
480     std::cout << "lock: entered in " << this << std::endl;
481
482   if (d_parent_detail)
483     d_parent_detail->lock();
484   else
485     d_owner->lock();
486 }
487
488 void
489 gr_hier_block2_detail::unlock()
490 {
491   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
492     std::cout << "unlock: entered in " << this << std::endl;
493
494   if (d_parent_detail)
495     d_parent_detail->unlock();
496   else
497     d_owner->unlock();
498 }