Merging r11186:11273 from grc branch.
[debian/gnuradio] / grc / python / FlowGraph.py
1 """
2 Copyright 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 GNU Radio Companion is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 """
19
20 import expr_utils
21 from .. base.FlowGraph import FlowGraph as _FlowGraph
22 from Block import Block
23 from Connection import Connection
24 import re
25
26 _variable_matcher = re.compile('^(variable\w*)$')
27 _parameter_matcher = re.compile('^(parameter)$')
28
29 class FlowGraph(_FlowGraph):
30
31         _eval_cache = dict()
32         def _eval(self, code, namespace, namespace_hash):
33                 """
34                 Evaluate the code with the given namespace.
35                 @param code a string with python code
36                 @param namespace a dict representing the namespace
37                 @param namespace_hash a unique hash for the namespace
38                 @return the resultant object
39                 """
40                 my_hash = hash(code) ^ namespace_hash
41                 #cache if does not exist
42                 if not self._eval_cache.has_key(my_hash):
43                         self._eval_cache[my_hash] = eval(code, namespace, namespace)
44                 #return from cache
45                 return self._eval_cache[my_hash]
46
47         def _get_io_signature(self, pad_key):
48                 """
49                 Get an io signature for this flow graph.
50                 The pad key determines the directionality of the io signature.
51                 @param pad_key a string of pad_source or pad_sink
52                 @return a dict with: type, nports, vlen, size
53                 """
54                 pads = filter(lambda b: b.get_key() == pad_key, self.get_enabled_blocks())
55                 if not pads: return {
56                         'nports': '0',
57                         'type': '',
58                         'vlen': '0',
59                         'size': '0',
60                 }
61                 pad = pads[0] #take only the first, user should not have more than 1
62                 #load io signature
63                 return {
64                         'nports': str(pad.get_param('nports').get_evaluated()),
65                         'type': str(pad.get_param('type').get_evaluated()),
66                         'vlen': str(pad.get_param('vlen').get_evaluated()),
67                         'size': pad.get_param('type').get_opt('size'),
68                 }
69
70         def get_input_signature(self):
71                 """
72                 Get the io signature for the input side of this flow graph.
73                 The io signature with be "0", "0" if no pad source is present.
74                 @return a string tuple of type, num_ports, port_size
75                 """
76                 return self._get_io_signature('pad_source')
77
78         def get_output_signature(self):
79                 """
80                 Get the io signature for the output side of this flow graph.
81                 The io signature with be "0", "0" if no pad sink is present.
82                 @return a string tuple of type, num_ports, port_size
83                 """
84                 return self._get_io_signature('pad_sink')
85
86         def get_imports(self):
87                 """
88                 Get a set of all import statments in this flow graph namespace.
89                 @return a set of import statements
90                 """
91                 imports = sum([block.get_imports() for block in self.get_enabled_blocks()], [])
92                 imports = sorted(set(imports))
93                 return imports
94
95         def get_variables(self):
96                 """
97                 Get a list of all variables in this flow graph namespace.
98                 Exclude paramterized variables.
99                 @return a sorted list of variable blocks in order of dependency (indep -> dep)
100                 """
101                 variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks())
102                 #map var id to variable block
103                 id2var = dict([(var.get_id(), var) for var in variables])
104                 #map var id to variable code
105                 #variable code is a concatenation of all param code (without the id param)
106                 id2expr = dict([(var.get_id(), var.get_var_make()) for var in variables])
107                 #sort according to dependency
108                 sorted_ids = expr_utils.sort_variables(id2expr)
109                 #create list of sorted variable blocks
110                 variables = [id2var[id] for id in sorted_ids]
111                 return variables
112
113         def get_parameters(self):
114                 """
115                 Get a list of all paramterized variables in this flow graph namespace.
116                 @return a list of paramterized variables
117                 """
118                 parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks())
119                 return parameters
120
121         def evaluate(self, expr):
122                 """
123                 Evaluate the expression.
124                 @param expr the string expression
125                 @throw Exception bad expression
126                 @return the evaluated data
127                 """
128                 if self.is_flagged():
129                         self.deflag()
130                         #reload namespace
131                         n = dict()
132                         #load imports
133                         for imp in self.get_imports():
134                                 try: exec imp in n
135                                 except: pass
136                         #load parameters
137                         np = dict()
138                         for parameter in self.get_parameters():
139                                 try:
140                                         e = eval(parameter.get_param('value').to_code(), n, n)
141                                         np[parameter.get_id()] = e
142                                 except: pass
143                         n.update(np) #merge param namespace
144                         #load variables
145                         for variable in self.get_variables():
146                                 try:
147                                         e = eval(variable.get_param('value').to_code(), n, n)
148                                         n[variable.get_id()] = e
149                                 except: pass
150                         #make namespace public
151                         self.n = n
152                         self.n_hash = hash(str(n))
153                 #evaluate
154                 e = self._eval(expr, self.n, self.n_hash)
155                 return e