Merge jcorgan/hier developer branch into trunk. Enables creation of true hierarchica...
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_hier_block2_detail.cc
1 /*
2  * Copyright 2006 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 2, 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 <stdexcept>
30 #include <iostream>
31
32 gr_hier_block2_detail::gr_hier_block2_detail(gr_hier_block2 *owner) :
33 d_owner(owner)
34 {
35 }
36
37 gr_hier_block2_detail::~gr_hier_block2_detail()
38 {
39     d_owner = 0; // Don't use delete, we didn't allocate
40 }
41
42 gr_basic_block_sptr
43 gr_hier_block2_detail::lookup_block(const std::string &name)
44 {
45     gr_hier_component_miter_t p = d_components.find(name);
46     if (p != d_components.end())
47         return p->second;
48     else
49         return gr_basic_block_sptr();
50 }
51
52 void 
53 gr_hier_block2_detail::define_component(const std::string &name, gr_basic_block_sptr block)
54 {
55     if (!block)
56         throw std::invalid_argument("null block passed");
57
58     if (name == "self")
59         throw std::invalid_argument("name is reserved");
60
61     // TODO: reject names with '.' inside
62     
63     if (!lookup_block(name))
64         d_components[name] = block;
65     else
66         throw std::invalid_argument("name already in use");
67 }
68
69 void 
70 gr_hier_block2_detail::connect(const std::string &src_name, int src_port, 
71                                const std::string &dst_name, int dst_port)
72 {
73     gr_io_signature_sptr src_io_signature;
74     gr_io_signature_sptr dst_io_signature;
75     
76     // Check against our *input_signature* if we're wiring from one of our external inputs
77     if (src_name == "self") 
78         src_io_signature = d_owner->input_signature();
79     else {
80         gr_basic_block_sptr src_block = lookup_block(src_name);
81         if (!src_block)
82             throw std::invalid_argument("undefined src name");
83         src_io_signature = src_block->output_signature();
84     }
85     
86     // Check against our *output_signature* if we're wiring to one of our external outputs
87     if (dst_name == "self") 
88         dst_io_signature = d_owner->output_signature();
89     else {
90         gr_basic_block_sptr dst_block = lookup_block(dst_name);
91         if (!dst_block)
92             throw std::invalid_argument("undefined dst name");
93         dst_io_signature = dst_block->input_signature();
94     }
95     
96     // Check port numbers are valid
97     check_valid_port(src_io_signature, src_port);
98     check_valid_port(dst_io_signature, dst_port);
99
100     // Check destination port not already in use
101     check_dst_not_used(dst_name, dst_port);
102
103     // Check endpoint types match
104     check_type_match(src_io_signature, src_port, dst_io_signature, dst_port);
105
106     d_edges.push_back(gr_make_edge(src_name, src_port, dst_name, dst_port));
107 }
108
109 void 
110 gr_hier_block2_detail::check_valid_port(gr_io_signature_sptr sig, int port)
111 {
112     if (port < 0)
113         throw std::invalid_argument("port number must not be negative");
114         
115     if (sig->max_streams() >= 0 && port >= sig->max_streams())
116         throw std::invalid_argument("port number exceeds max streams");
117 }
118
119 void 
120 gr_hier_block2_detail::check_dst_not_used(const std::string name, int port)
121 {
122     for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
123         if ((*p)->dst_name() == name && (*p)->dst_port() == port)
124             throw std::invalid_argument("destination port in use");
125 }
126
127 void 
128 gr_hier_block2_detail::check_type_match(gr_io_signature_sptr src_sig, int src_port,
129                                         gr_io_signature_sptr dst_sig, int dst_port)
130 {
131     if (src_sig->sizeof_stream_item(src_port) != dst_sig->sizeof_stream_item(dst_port))
132         throw std::invalid_argument("type mismatch");
133 }
134
135 std::string
136 gr_hier_block2_detail::prepend_prefix(const std::string &prefix, const std::string &str)
137 {
138     return prefix + ((prefix == "") ? "" : ".") + str;
139 }
140
141 gr_endpoint
142 gr_hier_block2_detail::match_endpoint(const std::string &name, int port, bool is_input)
143 {
144     for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
145         if (is_input) {
146             if ((*p)->src_name() == name && (*p)->src_port() == port)
147                 return resolve_endpoint((*p)->dst_name(), (*p)->dst_port(), "", !is_input);
148             }
149         else {
150             if ((*p)->dst_name() == name && (*p)->dst_port() == port)
151                 return resolve_endpoint((*p)->src_name(), (*p)->src_port(), "", !is_input);
152         }
153     }
154
155     // Should never get here
156     throw std::runtime_error("unable to match endpoint");
157 }
158
159 gr_endpoint
160 gr_hier_block2_detail::resolve_endpoint(const std::string &name, int port, 
161                                         const std::string &prefix, bool is_input)
162 {
163     gr_basic_block_sptr basic_block = lookup_block(name);
164
165     // Check if 'name' points to gr_block (leaf node)
166     gr_block_sptr block(boost::dynamic_pointer_cast<gr_block, gr_basic_block>(basic_block));
167     if (block)
168         return gr_endpoint(prepend_prefix(prefix, name), port);
169
170     // Check if 'name' points to hierarchical block
171     gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(basic_block));
172     if (hier_block2) {
173         std::string child_prefix = prepend_prefix(prefix, name);
174         gr_endpoint match(hier_block2->d_detail->match_endpoint("self", port, !is_input));
175         return gr_endpoint(prepend_prefix(child_prefix, match.name()), match.port());
176     }
177
178     // Shouldn't ever get here
179     throw std::runtime_error("unable to resolve endpoint");
180 }
181
182 void
183 gr_hier_block2_detail::flatten(gr_simple_flowgraph_sptr sfg, const std::string &prefix)
184 {
185     flatten_components(sfg, prefix);
186     flatten_edges(sfg, prefix);
187 }
188
189 void
190 gr_hier_block2_detail::flatten_components(gr_simple_flowgraph_sptr sfg, const std::string &prefix)
191 {
192     // Add my non-hierarchical components to the simple flowgraph, then recurse
193     for (gr_hier_component_miter_t p = d_components.begin(); p != d_components.end(); p++) {
194         std::string name = prepend_prefix(prefix, p->first);
195
196         gr_basic_block_sptr basic_block = p->second;
197         gr_block_sptr block(boost::dynamic_pointer_cast<gr_block, gr_basic_block>(basic_block));
198         if (block)      
199             sfg->define_component(name, block);
200
201         gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(basic_block));
202         if (hier_block2)
203             hier_block2->d_detail->flatten_components(sfg, name);
204     }
205 }
206
207 void
208 gr_hier_block2_detail::flatten_edges(gr_simple_flowgraph_sptr sfg, const std::string &prefix)
209 {
210     // Add my edges to the flow graph, resolving references to actual endpoints
211     for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
212         // Connections to self get resolved/added by parent if actually connected
213         if ((*p)->src_name() == "self" || (*p)->dst_name() == "self")
214             continue;
215
216         gr_endpoint src_endp = resolve_endpoint((*p)->src_name(), (*p)->src_port(), prefix, true);
217         gr_endpoint dst_endp = resolve_endpoint((*p)->dst_name(), (*p)->dst_port(), prefix, false);
218         sfg->connect(src_endp.name(), src_endp.port(), dst_endp.name(), dst_endp.port());
219     }
220
221     // Recurse hierarchical children
222     for (gr_hier_component_miter_t p = d_components.begin(); p != d_components.end(); p++) {
223         gr_hier_block2_sptr hier_block2(boost::dynamic_pointer_cast<gr_hier_block2, gr_basic_block>(p->second));
224         if (hier_block2)
225             hier_block2->d_detail->flatten_edges(sfg, prepend_prefix(prefix, p->first));
226     }
227 }