Merged features/mp-sched -r8915:9335 into the trunk. The trunk now
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_hier_block2_detail.cc
1 /*
2  * Copyright 2006,2007 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   d_inputs(owner->input_signature()->max_streams()),
38   d_outputs(owner->output_signature()->max_streams())
39 {
40 }
41
42 gr_hier_block2_detail::~gr_hier_block2_detail()
43 {
44   d_owner = 0; // Don't use delete, we didn't allocate
45 }
46
47 void
48 gr_hier_block2_detail::connect(gr_basic_block_sptr block)
49 {
50   std::stringstream msg;
51
52   // Check if duplicate
53   if (std::find(d_blocks.begin(), d_blocks.end(), block) != d_blocks.end()) {
54     msg << "Block " << block << " already connected.";
55     throw std::invalid_argument(msg.str());
56   }
57
58   // Check if has inputs or outputs
59   if (block->input_signature()->max_streams() != 0 ||
60       block->output_signature()->max_streams() != 0) {
61     msg << "Block " << block << " must not have any input or output ports";
62     throw std::invalid_argument(msg.str());
63   }
64
65   d_blocks.push_back(block);
66 }
67
68 void 
69 gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port, 
70                                gr_basic_block_sptr dst, int dst_port)
71 {
72   std::stringstream msg;
73   
74   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
75     std::cout << "connecting: " << gr_endpoint(src, src_port)
76               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
77
78   if (src.get() == dst.get())
79     throw std::invalid_argument("connect: src and destination blocks cannot be the same");
80
81   gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
82   gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
83
84   if (src_block && src.get() != d_owner) {
85     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
86       std::cout << "connect: src is hierarchical, setting parent to " << this << std::endl;
87     src_block->d_detail->d_parent_detail = this;
88   }
89                 
90   if (dst_block && dst.get() != d_owner) {
91     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
92       std::cout << "connect: dst is hierarchical, setting parent to " << this << std::endl;
93     dst_block->d_detail->d_parent_detail = this;
94   }
95
96   // Connections to block inputs or outputs
97   int max_port;
98   if (src.get() == d_owner) {
99     max_port = src->input_signature()->max_streams();
100     if ((max_port != -1 && (src_port >= max_port)) || src_port < 0) {
101       msg << "source port " << src_port << " out of range for " << src;
102       throw std::invalid_argument(msg.str());
103     }
104
105     return connect_input(src_port, dst_port, dst);
106   }
107
108   if (dst.get() == d_owner) {
109     max_port = dst->output_signature()->max_streams();
110     if ((max_port != -1 && (dst_port >= max_port)) || dst_port < 0) {
111       msg << "destination port " << dst_port << " out of range for " << dst;
112       throw std::invalid_argument(msg.str());
113     }
114
115     return connect_output(dst_port, src_port, src);
116   }
117
118   // Internal connections
119   d_fg->connect(src, src_port, dst, dst_port);
120
121   // TODO: connects to NC
122 }
123
124 void
125 gr_hier_block2_detail::disconnect(gr_basic_block_sptr block)
126 {
127   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
128     if (*p == block) {
129       d_blocks.erase(p);
130       return;
131     }
132   }
133
134   std::stringstream msg;
135   msg << "cannot disconnect block " << block << ", not found";
136   throw std::invalid_argument(msg.str());
137 }
138
139 void 
140 gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port, 
141                                   gr_basic_block_sptr dst, int dst_port)
142 {
143   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
144     std::cout << "disconnecting: " << gr_endpoint(src, src_port)
145               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
146
147   if (src.get() == dst.get())
148     throw std::invalid_argument("disconnect: source and destination blocks cannot be the same");
149
150   gr_hier_block2_sptr src_block(cast_to_hier_block2_sptr(src));
151   gr_hier_block2_sptr dst_block(cast_to_hier_block2_sptr(dst));
152
153   if (src_block && src.get() != d_owner) {
154     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
155       std::cout << "connect: src is hierarchical, clearing parent" << std::endl;
156     src_block->d_detail->d_parent_detail = 0;
157   }
158                 
159   if (dst_block && dst.get() != d_owner) {
160     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
161       std::cout << "connect: dst is hierarchical, clearing parent" << std::endl;
162     dst_block->d_detail->d_parent_detail = 0;
163   }
164
165   if (src.get() == d_owner)
166     return disconnect_input(src_port, dst_port, dst);
167
168   if (dst.get() == d_owner)
169     return disconnect_output(dst_port, src_port, src);
170
171   // Internal connections
172   d_fg->disconnect(src, src_port, dst, dst_port);
173 }
174
175 // FIXME: ticket:161 will be implemented here
176 void
177 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
178 {
179   std::stringstream msg;
180
181   if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
182     msg << "input port " << my_port << " out of range for " << block;
183     throw std::invalid_argument(msg.str());
184   }
185
186   if (d_inputs[my_port].block()) {
187     msg << "external input port " << my_port << " already wired to "
188         << d_inputs[my_port];
189     throw std::invalid_argument(msg.str());
190   }
191
192   d_inputs[my_port] = gr_endpoint(block, port);
193 }
194
195 void
196 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
197 {
198   std::stringstream msg;
199
200   if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
201     msg << "output port " << my_port << " out of range for " << block;
202     throw std::invalid_argument(msg.str());
203   }
204
205   if (d_outputs[my_port].block()) {
206     msg << "external output port " << my_port << " already connected from "
207         << d_outputs[my_port];
208     throw std::invalid_argument(msg.str());
209   }
210
211   d_outputs[my_port] = gr_endpoint(block, port);
212 }
213
214 void
215 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
216 {
217   std::stringstream msg;
218
219   if (my_port < 0 || my_port >= (signed)d_inputs.size()) {
220     msg << "input port number " << my_port << " out of range for " << block;
221     throw std::invalid_argument(msg.str());
222   }
223
224   if (d_inputs[my_port].block() != block) {
225     msg << "block " << block << " not assigned to input " 
226         << my_port << ", can't disconnect";
227     throw std::invalid_argument(msg.str());
228   }
229
230   d_inputs[my_port] = gr_endpoint();
231 }
232
233 void
234 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
235 {
236   std::stringstream msg;
237
238   if (my_port < 0 || my_port >= (signed)d_outputs.size()) {
239     msg << "output port number " << my_port << " out of range for " << block;
240     throw std::invalid_argument(msg.str());
241   }
242
243   if (d_outputs[my_port].block() != block) {
244     msg << "block " << block << " not assigned to output " 
245         << my_port << ", can't disconnect";
246     throw std::invalid_argument(msg.str());
247   }
248
249   d_outputs[my_port] = gr_endpoint();
250 }
251
252 gr_endpoint
253 gr_hier_block2_detail::resolve_port(int port, bool is_input)
254 {
255   std::stringstream msg;
256
257   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
258     std::cout << "Resolving port " << port << " as an "
259               << (is_input ? "input" : "output")
260               << " of " << d_owner->name() << std::endl;
261
262   gr_endpoint result;
263
264   if (is_input) {
265     if (port < 0 || port >= (signed)d_inputs.size()) {
266       msg << "resolve_port: input " << port << " is out of range";
267       throw std::runtime_error(msg.str());
268     }
269
270     result = resolve_endpoint(d_inputs[port], true);
271   }
272   else {
273     if (port < 0 || port >= (signed)d_outputs.size()) {
274       msg << "resolve_port: output " << port << " is out of range";
275       throw std::runtime_error(msg.str());
276     }
277
278     result = resolve_endpoint(d_outputs[port], false);
279   }
280
281   if (!result.block()) {
282     msg << "unable to resolve " 
283         << (is_input ? "input port " : "output port ")
284         << port;
285     throw std::runtime_error(msg.str());
286   }
287
288   return result;
289 }
290
291 void
292 gr_hier_block2_detail::disconnect_all()
293 {
294   d_fg->clear();
295   d_blocks.clear();
296   d_inputs.clear();
297   d_outputs.clear();
298 }
299
300 gr_endpoint
301 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input) const
302 {
303   std::stringstream msg;
304
305   // Check if endpoint is a leaf node
306   if (cast_to_block_sptr(endp.block()))
307     return endp;
308   
309   // Check if endpoint is a hierarchical block
310   gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(endp.block()));
311   if (hier_block2) {
312     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
313       std::cout << "Resolving endpoint " << endp << " as an " 
314                 << (is_input ? "input" : "output")
315                 << ", recursing" << std::endl;
316     return hier_block2->d_detail->resolve_port(endp.port(), is_input);
317   }
318
319   msg << "unable to resolve" << (is_input ? " input " : " output ")
320       << "endpoint " << endp;
321   throw std::runtime_error(msg.str());
322 }
323
324 void
325 gr_hier_block2_detail::flatten_aux(gr_flat_flowgraph_sptr sfg) const
326 {
327   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
328     std::cout << "flattening " << d_owner->name() << std::endl;
329
330   // Add my edges to the flow graph, resolving references to actual endpoints
331   gr_edge_vector_t edges = d_fg->edges();
332
333   for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
334     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
335       std::cout << "Flattening edge " << (*p) << std::endl;
336
337     gr_endpoint src_endp = resolve_endpoint(p->src(), false);
338     gr_endpoint dst_endp = resolve_endpoint(p->dst(), true);
339     sfg->connect(src_endp, dst_endp);
340   }
341
342   // Construct unique list of blocks used either in edges or
343   // by themselves.  I hate STL.
344   gr_basic_block_vector_t blocks, tmp = d_fg->calc_used_blocks();
345   std::insert_iterator<gr_basic_block_vector_t> inserter(blocks, blocks.begin());
346   std::vector<gr_basic_block_sptr>::const_iterator p; // Because flatten_aux is const
347   for (p = d_blocks.begin(); p != d_blocks.end(); p++) 
348     tmp.push_back(*p);
349   sort(tmp.begin(), tmp.end());
350   unique_copy(tmp.begin(), tmp.end(), inserter);
351
352   // Recurse hierarchical children
353   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
354     gr_hier_block2_sptr hier_block2(cast_to_hier_block2_sptr(*p));
355     if (hier_block2)
356       hier_block2->d_detail->flatten_aux(sfg);
357   }
358 }
359
360 void
361 gr_hier_block2_detail::lock()
362 {
363   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
364     std::cout << "lock: entered in " << this << std::endl;
365
366   if (d_parent_detail)
367     d_parent_detail->lock();
368   else
369     d_owner->lock();
370 }
371
372 void
373 gr_hier_block2_detail::unlock()
374 {
375   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
376     std::cout << "unlock: entered in " << this << std::endl;
377
378   if (d_parent_detail)
379     d_parent_detail->unlock();
380   else
381     d_owner->unlock();
382 }