d85d2674971719e95f5b27b012d96410f741caed
[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 = 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 // FIXME: ticket:161 will be implemented here
226 void
227 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
228 {
229   std::stringstream msg;
230
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());
234   }
235
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());
240   }
241
242   d_inputs[my_port] = gr_endpoint(block, port);
243 }
244
245 void
246 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
247 {
248   std::stringstream msg;
249
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());
253   }
254
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());
259   }
260
261   d_outputs[my_port] = gr_endpoint(block, port);
262 }
263
264 void
265 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
266 {
267   std::stringstream msg;
268
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());
272   }
273
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());
278   }
279
280   d_inputs[my_port] = gr_endpoint();
281 }
282
283 void
284 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
285 {
286   std::stringstream msg;
287
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());
291   }
292
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());
297   }
298
299   d_outputs[my_port] = gr_endpoint();
300 }
301
302 gr_endpoint
303 gr_hier_block2_detail::resolve_port(int port, bool is_input)
304 {
305   std::stringstream msg;
306
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;
311
312   gr_endpoint result;
313
314   if (is_input) {
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());
318     }
319
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());
324     }
325
326     result = resolve_endpoint(d_inputs[port], true);
327   }
328   else {
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());
332     }
333
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());
338     }
339
340     result = resolve_endpoint(d_outputs[port], false);
341   }
342
343   if (!result.block()) {
344     msg << "unable to resolve " 
345         << (is_input ? "input port " : "output port ")
346         << port;
347     throw std::runtime_error(msg.str());
348   }
349
350   return result;
351 }
352
353 void
354 gr_hier_block2_detail::disconnect_all()
355 {
356   d_fg->clear();
357   d_blocks.clear();
358   d_inputs.clear();
359   d_outputs.clear();
360 }
361
362 gr_endpoint
363 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
364 {
365   std::stringstream msg;
366
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;
371     return endp;
372   }
373
374   // Check if endpoint is a hierarchical block
375   gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
376   if (hier_block2) {
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);
382   }
383
384   msg << "unable to resolve" << (is_input ? " input " : " output ")
385       << "endpoint " << endp;
386   throw std::runtime_error(msg.str());
387 }
388
389 void
390 gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
391 {
392   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
393     std::cout << "Flattening " << d_owner->name() << std::endl;
394
395   // Add my edges to the flow graph, resolving references to actual endpoints
396   gr_edge_vector_t edges = d_fg->edges();
397   
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;
401
402     gr_endpoint src_endp = resolve_endpoint(p->src(), false);
403     gr_endpoint dst_endp = resolve_endpoint(p->dst(), true);
404
405     if (GR_HIER_BLOCK2_DETAIL_DEBUG) {
406       std::cout << "src_endp = " << src_endp 
407                 << ", dst_endp = " << dst_endp << std::endl;
408     }
409
410     sfg->connect(src_endp, dst_endp);
411   }
412
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();
416
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++) 
419     tmp.push_back(*p);
420
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());
426
427   sort(tmp.begin(), tmp.end());
428
429   std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
430   unique_copy(tmp.begin(), tmp.end(), inserter);
431
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));
435     if (hier_block2) {
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);
439     }
440   }
441 }
442
443 void
444 gr_hier_block2_detail::lock()
445 {
446   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
447     std::cout << "lock: entered in " << this << std::endl;
448
449   if (d_parent_detail)
450     d_parent_detail->lock();
451   else
452     d_owner->lock();
453 }
454
455 void
456 gr_hier_block2_detail::unlock()
457 {
458   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
459     std::cout << "unlock: entered in " << this << std::endl;
460
461   if (d_parent_detail)
462     d_parent_detail->unlock();
463   else
464     d_owner->unlock();
465 }