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