Merge r6160:6168 from jcorgan/fg into trunk.
[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 <gr_runtime.h>
29 #include <stdexcept>
30 #include <iostream>
31
32 #define GR_HIER_BLOCK2_DETAIL_DEBUG 0
33
34 gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) :
35   d_owner(owner), 
36   d_parent_detail(0),
37   d_fg(gr_make_flowgraph()),
38   d_inputs(owner->input_signature()->max_streams()),
39   d_outputs(owner->output_signature()->max_streams()),
40   d_runtime()
41 {
42 }
43
44 gr_hier_block2_detail::~gr_hier_block2_detail()
45 {
46   d_owner = 0; // Don't use delete, we didn't allocate
47 }
48
49 void 
50 gr_hier_block2_detail::connect(gr_basic_block_sptr src, int src_port, 
51                                gr_basic_block_sptr dst, int dst_port)
52 {
53   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
54     std::cout << "connecting: " << gr_endpoint(src, src_port)
55               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
56
57   if (src.get() == dst.get())
58     throw std::invalid_argument("src and destination blocks cannot be the same");
59
60   gr_hier_block2_sptr src_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(src));
61   gr_hier_block2_sptr dst_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(dst));
62
63   if (src_block && src.get() != d_owner) {
64     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
65       std::cout << "connect: src is hierarchical, setting parent to " << this << std::endl;
66     src_block->d_detail->d_parent_detail = this;
67   }
68                 
69   if (dst_block && dst.get() != d_owner) {
70     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
71       std::cout << "connect: dst is hierarchical, setting parent to " << this << std::endl;
72     dst_block->d_detail->d_parent_detail = this;
73   }
74
75   // Connections to block inputs or outputs
76   int max_port;
77   if (src.get() == d_owner) {
78     max_port = src->input_signature()->max_streams();
79     if ((max_port != -1 && (src_port >= max_port)) || src_port < 0)
80       throw std::invalid_argument("source port out of range");
81     return connect_input(src_port, dst_port, dst);
82   }
83
84   if (dst.get() == d_owner) {
85     max_port = dst->output_signature()->max_streams();
86     if ((max_port != -1 && (dst_port >= max_port)) || dst_port < 0)
87       throw std::invalid_argument("source port out of range");
88     return connect_output(dst_port, src_port, src);
89   }
90
91   // Internal connections
92   d_fg->connect(src, src_port, dst, dst_port);
93
94   // TODO: connects to NC
95 }
96
97 void 
98 gr_hier_block2_detail::disconnect(gr_basic_block_sptr src, int src_port, 
99                                   gr_basic_block_sptr dst, int dst_port)
100 {
101   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
102     std::cout << "disconnecting: " << gr_endpoint(src, src_port)
103               << " -> " << gr_endpoint(dst, dst_port) << std::endl;
104
105   if (src.get() == dst.get())
106     throw std::invalid_argument("src and destination blocks cannot be the same");
107
108   gr_hier_block2_sptr src_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(src));
109   gr_hier_block2_sptr dst_block(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(dst));
110
111   if (src_block && src.get() != d_owner) {
112     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
113       std::cout << "connect: src is hierarchical, clearing parent" << std::endl;
114     src_block->d_detail->d_parent_detail = 0;
115   }
116                 
117   if (dst_block && dst.get() != d_owner) {
118     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
119       std::cout << "connect: dst is hierarchical, clearing parent" << std::endl;
120     dst_block->d_detail->d_parent_detail = 0;
121   }
122
123   if (src.get() == d_owner)
124     return disconnect_input(src_port, dst_port, dst);
125
126   if (dst.get() == d_owner)
127     return disconnect_output(dst_port, src_port, src);
128
129   // Internal connections
130   d_fg->disconnect(src, src_port, dst, dst_port);
131 }
132
133 void
134 gr_hier_block2_detail::connect_input(int my_port, int port, gr_basic_block_sptr block)
135 {
136   if (my_port < 0 || my_port >= (signed)d_inputs.size())
137     throw std::invalid_argument("input port number out of range");
138
139   if (d_inputs[my_port].block())
140     throw std::invalid_argument("input port in use");
141
142   d_inputs[my_port] = gr_endpoint(block, port);
143 }
144
145 void
146 gr_hier_block2_detail::connect_output(int my_port, int port, gr_basic_block_sptr block)
147 {
148   if (my_port < 0 || my_port >= (signed)d_outputs.size())
149     throw std::invalid_argument("output port number out of range");
150
151   if (d_outputs[my_port].block())
152     throw std::invalid_argument("output port in use");
153
154   d_outputs[my_port] = gr_endpoint(block, port);
155 }
156
157 void
158 gr_hier_block2_detail::disconnect_input(int my_port, int port, gr_basic_block_sptr block)
159 {
160   if (my_port < 0 || my_port >= (signed)d_inputs.size())
161     throw std::invalid_argument("input port number out of range");
162
163   if (d_inputs[my_port].block() != block)
164     throw std::invalid_argument("block not assigned to given input, can't disconnect");
165
166   d_inputs[my_port] = gr_endpoint();
167 }
168
169 void
170 gr_hier_block2_detail::disconnect_output(int my_port, int port, gr_basic_block_sptr block)
171 {
172   if (my_port < 0 || my_port >= (signed)d_outputs.size())
173     throw std::invalid_argument("input port number out of range");
174
175   if (d_outputs[my_port].block() != block)
176     throw std::invalid_argument("block not assigned to given output, can't disconnect");
177
178   d_outputs[my_port] = gr_endpoint();
179 }
180
181 gr_endpoint
182 gr_hier_block2_detail::resolve_port(int port, bool is_input)
183 {
184   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
185     std::cout << "Resolving port " << port << " as an "
186               << (is_input ? "input" : "output")
187               << " of " << d_owner->name() << std::endl;
188
189   gr_endpoint result;
190
191   if (is_input) {
192     if (port < 0 || port >= (signed)d_inputs.size())
193       throw std::runtime_error("input port number out of range");
194     result = resolve_endpoint(d_inputs[port], true);
195   }
196   else {
197     if (port < 0 || port >= (signed)d_outputs.size())
198       throw std::runtime_error("output port number out of range");
199     result = resolve_endpoint(d_outputs[port], false);
200   }
201
202   if (!result.block())
203     throw std::runtime_error("unable to resolve port");
204
205   return result;
206 }
207
208 gr_endpoint
209 gr_hier_block2_detail::resolve_endpoint(const gr_endpoint &endp, bool is_input)
210 {
211   // Check if endpoint is a leaf node
212   if (boost::dynamic_pointer_cast<gr_block, gr_basic_block>(endp.block()))
213     return endp;
214   
215   // Check if endpoint is a hierarchical block
216   gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(endp.block()));
217   if (hier_block2) {
218     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
219       std::cout << "Resolving endpoint " << endp << " as an " 
220                 << (is_input ? "input" : "output")
221                 << ", recursing" << std::endl;
222     return hier_block2->d_detail->resolve_port(endp.port(), is_input);
223   }
224
225   // Shouldn't ever get here
226   throw std::runtime_error("block is not a valid gr_block or gr_hier_block2!");
227 }
228
229 void
230 gr_hier_block2_detail::flatten(gr_flat_flowgraph_sptr sfg)
231 {
232   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
233     std::cout << "flattening " << d_owner->name() << std::endl;
234
235   // Add my edges to the flow graph, resolving references to actual endpoints
236   gr_edge_vector_t edges = d_fg->edges();
237
238   for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
239     if (GR_HIER_BLOCK2_DETAIL_DEBUG)
240       std::cout << "Flattening edge " << (*p) << std::endl;
241
242     gr_endpoint src_endp = resolve_endpoint(p->src(), false);
243     gr_endpoint dst_endp = resolve_endpoint(p->dst(), true);
244     sfg->connect(src_endp, dst_endp);
245   }
246
247   gr_basic_block_vector_t blocks = d_fg->calc_used_blocks();
248
249   // Recurse hierarchical children
250   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
251     gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(*p));
252     if (hier_block2)
253       hier_block2->d_detail->flatten(sfg);
254   }
255 }
256
257 void
258 gr_hier_block2_detail::lock()
259 {
260   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
261     std::cout << "lock: entered in " << this << std::endl;
262
263   if (d_parent_detail)
264     d_parent_detail->lock();
265   else
266     if (d_runtime)
267       d_runtime->lock();
268 }
269
270 void
271 gr_hier_block2_detail::unlock()
272 {
273   if (GR_HIER_BLOCK2_DETAIL_DEBUG)
274     std::cout << "unlock: entered in " << this << std::endl;
275
276   if (d_parent_detail)
277     d_parent_detail->unlock();
278   else
279     if (d_runtime)
280       d_runtime->unlock();
281 }