3
* Copyright 2007 Free Software Foundation, Inc.
5
* This file is part of GNU Radio
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)
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.
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.
27
#include <gr_flowgraph.h>
28
#include <gr_io_signature.h>
32
#define GR_FLOWGRAPH_DEBUG 0
38
gr_flowgraph_sptr gr_make_flowgraph()
40
return gr_flowgraph_sptr(new gr_flowgraph());
43
gr_flowgraph::gr_flowgraph()
47
gr_flowgraph::~gr_flowgraph()
51
// FIXME: move to libgruel as a utility function
55
unique_vector(std::vector<T> v)
57
std::vector<T> result;
58
std::insert_iterator<std::vector<T> > inserter(result, result.begin());
60
sort(v.begin(), v.end());
61
unique_copy(v.begin(), v.end(), inserter);
66
gr_flowgraph::connect(const gr_endpoint &src, const gr_endpoint &dst)
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);
73
// All ist klar, Herr Kommisar
74
d_edges.push_back(gr_edge(src,dst));
78
gr_flowgraph::disconnect(const gr_endpoint &src, const gr_endpoint &dst)
80
for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
81
if (src == p->src() && dst == p->dst()) {
87
std::stringstream msg;
88
msg << "cannot disconnect edge " << gr_edge(src, dst) << ", not found";
89
throw std::invalid_argument(msg.str());
93
gr_flowgraph::validate()
95
d_blocks = calc_used_blocks();
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;
101
if (GR_FLOWGRAPH_DEBUG)
102
std::cout << "Validating block: " << (*p) << std::endl;
104
used_ports = calc_used_ports(*p, true); // inputs
105
ninputs = used_ports.size();
106
check_contiguity(*p, used_ports, true); // inputs
108
used_ports = calc_used_ports(*p, false); // outputs
109
noutputs = used_ports.size();
110
check_contiguity(*p, used_ports, false); // outputs
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());
123
gr_flowgraph::clear()
125
// Boost shared pointers will deallocate as needed
131
gr_flowgraph::check_valid_port(gr_io_signature_sptr sig, int port)
133
std::stringstream msg;
136
msg << "negative port number " << port << " is invalid";
137
throw std::invalid_argument(msg.str());
140
int max = sig->max_streams();
141
if (max != gr_io_signature::IO_INFINITE && port >= max) {
142
msg << "port number " << port << " exceeds max of ";
147
throw std::invalid_argument(msg.str());
152
gr_flowgraph::check_dst_not_used(const gr_endpoint &dst)
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());
164
gr_flowgraph::check_type_match(const gr_endpoint &src, const gr_endpoint &dst)
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());
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());
177
gr_basic_block_vector_t
178
gr_flowgraph::calc_used_blocks()
180
gr_basic_block_vector_t tmp;
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());
188
return unique_vector<gr_basic_block_sptr>(tmp);
192
gr_flowgraph::calc_used_ports(gr_basic_block_sptr block, bool check_inputs)
194
std::vector<int> tmp;
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());
202
tmp.push_back(p->src().port());
205
return unique_vector<int>(tmp);
209
gr_flowgraph::calc_connections(gr_basic_block_sptr block, bool check_inputs)
211
gr_edge_vector_t result;
213
for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
215
if (p->dst().block() == block)
216
result.push_back(*p);
219
if (p->src().block() == block)
220
result.push_back(*p);
224
return result; // assumes no duplicates
228
gr_flowgraph::check_contiguity(gr_basic_block_sptr block,
229
const std::vector<int> &used_ports,
232
std::stringstream msg;
234
gr_io_signature_sptr sig =
235
check_inputs ? block->input_signature() : block->output_signature();
237
int nports = used_ports.size();
238
int min_ports = sig->min_streams();
239
int max_ports = sig->max_streams();
241
if (nports == 0 && min_ports == 0)
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());
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());
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 ")
264
throw std::runtime_error(msg.str());
270
gr_basic_block_vector_t
271
gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block, int port)
273
gr_basic_block_vector_t tmp;
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());
279
return unique_vector<gr_basic_block_sptr>(tmp);
282
gr_basic_block_vector_t
283
gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block)
285
gr_basic_block_vector_t tmp;
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());
291
return unique_vector<gr_basic_block_sptr>(tmp);
295
gr_flowgraph::calc_upstream_edges(gr_basic_block_sptr block)
297
gr_edge_vector_t result;
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);
303
return result; // Assume no duplicates
307
gr_flowgraph::has_block_p(gr_basic_block_sptr block)
309
gr_basic_block_viter_t result;
310
result = std::find(d_blocks.begin(), d_blocks.end(), block);
311
return (result != d_blocks.end());
315
gr_flowgraph::calc_upstream_edge(gr_basic_block_sptr block, int port)
319
for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
320
if (p->dst() == gr_endpoint(block, port)) {
329
std::vector<gr_basic_block_vector_t>
330
gr_flowgraph::partition()
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;
336
while (blocks.size() > 0) {
337
graph = calc_reachable_blocks(blocks[0], blocks);
338
assert(graph.size());
339
result.push_back(topological_sort(graph));
341
for (gr_basic_block_viter_t p = graph.begin(); p != graph.end(); p++)
342
blocks.erase(find(blocks.begin(), blocks.end(), *p));
348
gr_basic_block_vector_t
349
gr_flowgraph::calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
351
gr_basic_block_vector_t result;
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);
357
// Recursively mark all reachable blocks
358
reachable_dfs_visit(block, blocks);
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);
368
// Recursively mark all reachable blocks from given block and block list
370
gr_flowgraph::reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
372
// Mark the current one as visited
373
block->set_color(gr_basic_block::BLACK);
375
// Recurse into adjacent vertices
376
gr_basic_block_vector_t adjacent = calc_adjacent_blocks(block, blocks);
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);
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)
387
gr_basic_block_vector_t tmp;
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());
397
return unique_vector<gr_basic_block_sptr>(tmp);
400
gr_basic_block_vector_t
401
gr_flowgraph::topological_sort(gr_basic_block_vector_t &blocks)
403
gr_basic_block_vector_t tmp;
404
gr_basic_block_vector_t result;
405
tmp = sort_sources_first(blocks);
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);
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);
416
reverse(result.begin(), result.end());
420
gr_basic_block_vector_t
421
gr_flowgraph::sort_sources_first(gr_basic_block_vector_t &blocks)
423
gr_basic_block_vector_t sources, nonsources, result;
425
for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
427
sources.push_back(*p);
429
nonsources.push_back(*p);
432
for (gr_basic_block_viter_t p = sources.begin(); p != sources.end(); p++)
433
result.push_back(*p);
435
for (gr_basic_block_viter_t p = nonsources.begin(); p != nonsources.end(); p++)
436
result.push_back(*p);
442
gr_flowgraph::source_p(gr_basic_block_sptr block)
444
return (calc_upstream_edges(block).size() == 0);
448
gr_flowgraph::topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output)
450
block->set_color(gr_basic_block::GREY);
451
gr_basic_block_vector_t blocks(calc_downstream_blocks(block));
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);
459
case gr_basic_block::GREY:
460
throw std::runtime_error("flow graph has loops!");
462
case gr_basic_block::BLACK:
466
throw std::runtime_error("invalid color on block!");
470
block->set_color(gr_basic_block::BLACK);
471
output.push_back(block);