Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_flowgraph.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gr_flowgraph.h>
28 #include <gr_io_signature.h>
29 #include <stdexcept>
30 #include <sstream>
31
32 #define GR_FLOWGRAPH_DEBUG 0
33
34 gr_edge::~gr_edge()
35 {
36 }
37
38 gr_flowgraph_sptr gr_make_flowgraph()
39 {
40   return gr_flowgraph_sptr(new gr_flowgraph());
41 }
42
43 gr_flowgraph::gr_flowgraph()
44 {
45 }
46   
47 gr_flowgraph::~gr_flowgraph()
48 {
49 }
50
51 // FIXME: move to libgruel as a utility function
52 template<class T>
53 static
54 std::vector<T>
55 unique_vector(std::vector<T> v)
56 {
57   std::vector<T> result;
58   std::insert_iterator<std::vector<T> > inserter(result, result.begin());
59   
60   sort(v.begin(), v.end());
61   unique_copy(v.begin(), v.end(), inserter);
62   return result;
63 }
64
65 void
66 gr_flowgraph::connect(const gr_endpoint &src, const gr_endpoint &dst)
67 {
68   check_valid_port(src.block()->output_signature(), src.port());
69   check_valid_port(dst.block()->input_signature(), dst.port());
70   check_dst_not_used(dst);
71   check_type_match(src, dst);
72
73   // All ist klar, Herr Kommisar
74   d_edges.push_back(gr_edge(src,dst));
75 }
76
77 void
78 gr_flowgraph::disconnect(const gr_endpoint &src, const gr_endpoint &dst)
79 {
80   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
81     if (src == p->src() && dst == p->dst()) {
82       d_edges.erase(p);
83       return;
84     }
85   }
86
87   std::stringstream msg;
88   msg << "cannot disconnect edge " << gr_edge(src, dst) << ", not found";
89   throw std::invalid_argument(msg.str());
90 }
91
92 void
93 gr_flowgraph::validate()
94 {
95   d_blocks = calc_used_blocks();
96
97   for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
98     std::vector<int> used_ports;
99     int ninputs, noutputs;
100
101     if (GR_FLOWGRAPH_DEBUG)
102       std::cout << "Validating block: " << (*p) << std::endl;
103
104     used_ports = calc_used_ports(*p, true); // inputs
105     ninputs = used_ports.size();
106     check_contiguity(*p, used_ports, true); // inputs
107
108     used_ports = calc_used_ports(*p, false); // outputs
109     noutputs = used_ports.size();
110     check_contiguity(*p, used_ports, false); // outputs
111
112     if (!((*p)->check_topology(ninputs, noutputs))) {
113       std::stringstream msg;
114       msg << "check topology failed on " << (*p)
115           << " using ninputs=" << ninputs 
116           << ", noutputs=" << noutputs;
117       throw std::runtime_error(msg.str());
118     }
119   }
120 }
121
122 void
123 gr_flowgraph::clear()
124 {
125   // Boost shared pointers will deallocate as needed
126   d_blocks.clear();
127   d_edges.clear();
128 }
129
130 void
131 gr_flowgraph::check_valid_port(gr_io_signature_sptr sig, int port)
132 {
133   std::stringstream msg;
134
135   if (port < 0) {
136     msg << "negative port number " << port << " is invalid";
137     throw std::invalid_argument(msg.str());
138   }
139
140   int max = sig->max_streams();
141   if (max != gr_io_signature::IO_INFINITE && port >= max) {
142     msg << "port number " << port << " exceeds max of ";
143     if (max == 0)
144       msg << "(none)";
145     else
146       msg << max-1;
147     throw std::invalid_argument(msg.str());
148   }
149 }
150
151 void
152 gr_flowgraph::check_dst_not_used(const gr_endpoint &dst)
153 {
154   // A destination is in use if it is already on the edge list
155   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
156     if (p->dst() == dst) {
157       std::stringstream msg;
158       msg << "destination already in use by edge " << (*p);
159       throw std::invalid_argument(msg.str());
160     }
161 }
162
163 void
164 gr_flowgraph::check_type_match(const gr_endpoint &src, const gr_endpoint &dst)
165 {
166   int src_size = src.block()->output_signature()->sizeof_stream_item(src.port());
167   int dst_size = dst.block()->input_signature()->sizeof_stream_item(dst.port());
168
169   if (src_size != dst_size) {
170     std::stringstream msg;
171     msg << "itemsize mismatch: " << src << " using " << src_size
172         << ", " << dst << " using " << dst_size;
173     throw std::invalid_argument(msg.str());
174   }
175 }
176
177 gr_basic_block_vector_t
178 gr_flowgraph::calc_used_blocks()
179 {
180   gr_basic_block_vector_t tmp;
181
182   // Collect all blocks in the edge list
183   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
184     tmp.push_back(p->src().block());
185     tmp.push_back(p->dst().block());
186   }
187
188   return unique_vector<gr_basic_block_sptr>(tmp);
189 }
190
191 std::vector<int>
192 gr_flowgraph::calc_used_ports(gr_basic_block_sptr block, bool check_inputs)
193 {
194   std::vector<int> tmp;
195
196   // Collect all seen ports 
197   gr_edge_vector_t edges = calc_connections(block, check_inputs);
198   for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
199     if (check_inputs == true)
200       tmp.push_back(p->dst().port());
201     else
202       tmp.push_back(p->src().port());
203   }
204
205   return unique_vector<int>(tmp);
206 }
207
208 gr_edge_vector_t
209 gr_flowgraph::calc_connections(gr_basic_block_sptr block, bool check_inputs)
210 {
211   gr_edge_vector_t result;
212
213   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
214     if (check_inputs) {
215       if (p->dst().block() == block)
216         result.push_back(*p);
217     }
218     else {
219       if (p->src().block() == block)
220         result.push_back(*p);
221     }
222   }
223
224   return result; // assumes no duplicates
225 }
226
227 void
228 gr_flowgraph::check_contiguity(gr_basic_block_sptr block,
229                                const std::vector<int> &used_ports,
230                                bool check_inputs)
231 {
232   std::stringstream msg;
233
234   gr_io_signature_sptr sig =
235     check_inputs ? block->input_signature() : block->output_signature();
236
237   int nports = used_ports.size();
238   int min_ports = sig->min_streams();
239   int max_ports = sig->max_streams();
240
241   if (nports == 0 && min_ports == 0)
242     return;
243
244   if (nports < min_ports) {
245     msg << block << ": insufficient connected "
246        << (check_inputs ? "input ports " : "output ports ")
247        << "(" << min_ports << " needed, " << nports << " connected)";
248     throw std::runtime_error(msg.str());
249   }
250
251   if (nports > max_ports && max_ports != gr_io_signature::IO_INFINITE) {
252     msg << block << ": too many connected "
253        << (check_inputs ? "input ports " : "output ports ")
254        << "(" << max_ports << " allowed, " << nports << " connected)";
255     throw std::runtime_error(msg.str());
256   }
257
258   if (used_ports[nports-1]+1 != nports) {
259     for (int i = 0; i < nports; i++) {
260       if (used_ports[i] != i) {
261         msg << block << ": missing connection " 
262             << (check_inputs ? "to input port " : "from output port ")
263             << i;
264         throw std::runtime_error(msg.str());
265       }
266     }
267   }
268 }
269
270 gr_basic_block_vector_t
271 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block, int port)
272 {
273   gr_basic_block_vector_t tmp;
274
275   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
276     if (p->src() == gr_endpoint(block, port))
277       tmp.push_back(p->dst().block());
278
279   return unique_vector<gr_basic_block_sptr>(tmp);
280 }
281
282 gr_basic_block_vector_t
283 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block)
284 {
285   gr_basic_block_vector_t tmp;
286
287   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
288     if (p->src().block() == block)
289       tmp.push_back(p->dst().block());
290
291   return unique_vector<gr_basic_block_sptr>(tmp);
292 }
293
294 gr_edge_vector_t
295 gr_flowgraph::calc_upstream_edges(gr_basic_block_sptr block)
296 {
297   gr_edge_vector_t result;
298
299   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
300     if (p->dst().block() == block)
301       result.push_back(*p);
302
303   return result; // Assume no duplicates
304 }
305
306 bool
307 gr_flowgraph::has_block_p(gr_basic_block_sptr block)
308 {
309   gr_basic_block_viter_t result;
310   result = std::find(d_blocks.begin(), d_blocks.end(), block);
311   return (result != d_blocks.end());
312 }
313
314 gr_edge
315 gr_flowgraph::calc_upstream_edge(gr_basic_block_sptr block, int port)
316 {
317   gr_edge result;
318
319   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
320     if (p->dst() == gr_endpoint(block, port)) {
321       result = (*p);
322       break;
323     }
324   }
325
326   return result;
327 }
328
329 std::vector<gr_basic_block_vector_t>
330 gr_flowgraph::partition()
331 {
332   std::vector<gr_basic_block_vector_t> result;
333   gr_basic_block_vector_t blocks = calc_used_blocks();
334   gr_basic_block_vector_t graph;
335
336   while (blocks.size() > 0) {
337     graph = calc_reachable_blocks(blocks[0], blocks);
338     assert(graph.size());
339     result.push_back(topological_sort(graph));
340
341     for (gr_basic_block_viter_t p = graph.begin(); p != graph.end(); p++)
342       blocks.erase(find(blocks.begin(), blocks.end(), *p));
343   }
344
345   return result;
346 }
347
348 gr_basic_block_vector_t
349 gr_flowgraph::calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
350 {
351   gr_basic_block_vector_t result;
352
353   // Mark all blocks as unvisited
354   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
355     (*p)->set_color(gr_basic_block::WHITE);
356
357   // Recursively mark all reachable blocks
358   reachable_dfs_visit(block, blocks);
359
360   // Collect all the blocks that have been visited
361   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
362     if ((*p)->color() == gr_basic_block::BLACK)
363       result.push_back(*p);
364
365   return result;
366 }
367
368 // Recursively mark all reachable blocks from given block and block list
369 void 
370 gr_flowgraph::reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
371 {
372   // Mark the current one as visited
373   block->set_color(gr_basic_block::BLACK);
374
375   // Recurse into adjacent vertices
376   gr_basic_block_vector_t adjacent = calc_adjacent_blocks(block, blocks);
377
378   for (gr_basic_block_viter_t p = adjacent.begin(); p != adjacent.end(); p++)
379     if ((*p)->color() == gr_basic_block::WHITE)
380       reachable_dfs_visit(*p, blocks);
381 }
382
383 // Return a list of block adjacent to a given block along any edge
384 gr_basic_block_vector_t 
385 gr_flowgraph::calc_adjacent_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
386 {
387   gr_basic_block_vector_t tmp;
388     
389   // Find any blocks that are inputs or outputs
390   for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
391     if (p->src().block() == block)
392       tmp.push_back(p->dst().block());
393     if (p->dst().block() == block)
394       tmp.push_back(p->src().block());
395   }    
396
397   return unique_vector<gr_basic_block_sptr>(tmp);
398 }
399
400 gr_basic_block_vector_t
401 gr_flowgraph::topological_sort(gr_basic_block_vector_t &blocks)
402 {
403   gr_basic_block_vector_t tmp;
404   gr_basic_block_vector_t result;
405   tmp = sort_sources_first(blocks);
406
407   // Start 'em all white
408   for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++)
409     (*p)->set_color(gr_basic_block::WHITE);
410
411   for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) {
412     if ((*p)->color() == gr_basic_block::WHITE)
413       topological_dfs_visit(*p, result);
414   }    
415
416   reverse(result.begin(), result.end());
417   return result;
418 }
419
420 gr_basic_block_vector_t
421 gr_flowgraph::sort_sources_first(gr_basic_block_vector_t &blocks)
422 {
423   gr_basic_block_vector_t sources, nonsources, result;
424
425   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
426     if (source_p(*p))
427       sources.push_back(*p);
428     else
429       nonsources.push_back(*p);
430   }
431
432   for (gr_basic_block_viter_t p = sources.begin(); p != sources.end(); p++)
433     result.push_back(*p);
434
435   for (gr_basic_block_viter_t p = nonsources.begin(); p != nonsources.end(); p++)
436     result.push_back(*p);
437
438   return result;
439 }
440
441 bool
442 gr_flowgraph::source_p(gr_basic_block_sptr block)
443 {
444   return (calc_upstream_edges(block).size() == 0);
445 }
446
447 void
448 gr_flowgraph::topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output)
449 {
450   block->set_color(gr_basic_block::GREY);
451   gr_basic_block_vector_t blocks(calc_downstream_blocks(block));
452
453   for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
454     switch ((*p)->color()) {
455     case gr_basic_block::WHITE:           
456       topological_dfs_visit(*p, output);
457       break;
458
459     case gr_basic_block::GREY:            
460       throw std::runtime_error("flow graph has loops!");
461
462     case gr_basic_block::BLACK:
463       continue;
464
465     default:
466       throw std::runtime_error("invalid color on block!");
467     }
468   }
469
470   block->set_color(gr_basic_block::BLACK);
471   output.push_back(block);
472 }
473