added continuation mode for pad source and sinks
[debian/gnuradio] / grc / base / Block.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 from . import odict
21 from Element import Element
22 from Param import Param
23 from Port import Port
24
25 from Cheetah.Template import Template
26 from UserDict import UserDict
27
28 class TemplateArg(UserDict):
29         """
30         A cheetah template argument created from a param.
31         The str of this class evaluates to the param's to code method.
32         The use of this class as a dictionary (enum only) will reveal the enum opts.
33         The __call__ or () method can return the param evaluated to a raw python data type.
34         """
35
36         def __init__(self, param):
37                 UserDict.__init__(self)
38                 self._param = param
39                 if param.is_enum():
40                         for key in param.get_opt_keys():
41                                 self[key] = str(param.get_opt(key))
42
43         def __str__(self):
44                 return str(self._param.to_code())
45
46         def __call__(self):
47                 return self._param.get_evaluated()
48
49 def _get_keys(lst): return [elem.get_key() for elem in lst]
50 def _get_elem(lst, key):
51         try: return lst[_get_keys(lst).index(key)]
52         except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst))
53
54 class Block(Element):
55
56         def __init__(self, flow_graph, n):
57                 """
58                 Make a new block from nested data.
59                 @param flow graph the parent element
60                 @param n the nested odict
61                 @return block a new block
62                 """
63                 #build the block
64                 Element.__init__(self, flow_graph)
65                 #grab the data
66                 params = n.findall('param')
67                 sources = n.findall('source')
68                 sinks = n.findall('sink')
69                 self._name = n.find('name')
70                 self._key = n.find('key')
71                 self._category = n.find('category') or ''
72                 self._block_wrapper_path = n.find('block_wrapper_path')
73                 #create the param objects
74                 self._params = list()
75                 #add the id param
76                 self.get_params().append(self.get_parent().get_parent().Param(
77                         self,
78                         odict({
79                                 'name': 'ID',
80                                 'key': 'id',
81                                 'type': 'id',
82                         })
83                 ))
84                 self.get_params().append(self.get_parent().get_parent().Param(
85                         self,
86                         odict({
87                                 'name': 'Enabled',
88                                 'key': '_enabled',
89                                 'type': 'raw',
90                                 'value': 'True',
91                                 'hide': 'all',
92                         })
93                 ))
94                 for param in map(lambda n: self.get_parent().get_parent().Param(self, n), params):
95                         key = param.get_key()
96                         #test against repeated keys
97                         try: assert key not in self.get_param_keys()
98                         except AssertionError: raise Exception, 'Key "%s" already exists in params'%key
99                         #store the param
100                         self.get_params().append(param)
101                 #create the source objects
102                 self._sources = list()
103                 for source in map(lambda n: self.get_parent().get_parent().Port(self, n, dir='source'), sources):
104                         key = source.get_key()
105                         #test against repeated keys
106                         try: assert key not in self.get_source_keys()
107                         except AssertionError: raise Exception, 'Key "%s" already exists in sources'%key
108                         #store the port
109                         self.get_sources().append(source)
110                 #create the sink objects
111                 self._sinks = list()
112                 for sink in map(lambda n: self.get_parent().get_parent().Port(self, n, dir='sink'), sinks):
113                         key = sink.get_key()
114                         #test against repeated keys
115                         try: assert key not in self.get_sink_keys()
116                         except AssertionError: raise Exception, 'Key "%s" already exists in sinks'%key
117                         #store the port
118                         self.get_sinks().append(sink)
119                 #begin the testing
120                 self.test()
121
122         def test(self):
123                 """
124                 Call test on all children.
125                 """
126                 map(lambda c: c.test(), self.get_params() + self.get_sinks() + self.get_sources())
127
128         def get_enabled(self):
129                 """
130                 Get the enabled state of the block.
131                 @return true for enabled
132                 """
133                 try: return eval(self.get_param('_enabled').get_value())
134                 except: return True
135
136         def set_enabled(self, enabled):
137                 """
138                 Set the enabled state of the block.
139                 @param enabled true for enabled
140                 """
141                 self.get_param('_enabled').set_value(str(enabled))
142
143         def validate(self):
144                 """
145                 Validate the block.
146                 All ports and params must be valid.
147                 All checks must evaluate to true.
148                 """
149                 Element.validate(self)
150                 for c in self.get_params() + self.get_ports() + self.get_connections():
151                         try:
152                                 c.validate()
153                                 assert c.is_valid()
154                         except AssertionError:
155                                 for msg in c.get_error_messages():
156                                         self.add_error_message('>>> %s:\n\t%s'%(c, msg))
157
158         def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key())
159
160         def get_id(self): return self.get_param('id').get_value()
161         def is_block(self): return True
162         def get_name(self): return self._name
163         def get_key(self): return self._key
164         def get_category(self): return self._category
165         def get_doc(self): return ''
166         def get_ports(self): return self.get_sources() + self.get_sinks()
167         def get_block_wrapper_path(self): return self._block_wrapper_path
168
169         ##############################################
170         # Access Params
171         ##############################################
172         def get_param_keys(self): return _get_keys(self._params)
173         def get_param(self, key): return _get_elem(self._params, key)
174         def get_params(self): return self._params
175
176         ##############################################
177         # Access Sinks
178         ##############################################
179         def get_sink_keys(self): return _get_keys(self._sinks)
180         def get_sink(self, key): return _get_elem(self._sinks, key)
181         def get_sinks(self): return self._sinks
182
183         ##############################################
184         # Access Sources
185         ##############################################
186         def get_source_keys(self): return _get_keys(self._sources)
187         def get_source(self, key): return _get_elem(self._sources, key)
188         def get_sources(self): return self._sources
189
190         def get_connections(self):
191                 return sum([port.get_connections() for port in self.get_ports()], [])
192
193         def resolve_dependencies(self, tmpl):
194                 """
195                 Resolve a paramater dependency with cheetah templates.
196                 @param tmpl the string with dependencies
197                 @return the resolved value
198                 """
199                 tmpl = str(tmpl)
200                 if '$' not in tmpl: return tmpl
201                 n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params())
202                 try: return str(Template(tmpl, n))
203                 except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl)
204
205         ##############################################
206         # Controller Modify
207         ##############################################
208         def type_controller_modify(self, direction):
209                 """
210                 Change the type controller.
211                 @param direction +1 or -1
212                 @return true for change
213                 """
214                 changed = False
215                 type_param = None
216                 for param in filter(lambda p: p.is_enum(), self.get_params()):
217                         children = self.get_ports() + self.get_params()
218                         #priority to the type controller
219                         if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
220                         #use param if type param is unset
221                         if not type_param: type_param = param
222                 if type_param:
223                         #try to increment the enum by direction
224                         try:
225                                 keys = type_param.get_option_keys()
226                                 old_index = keys.index(type_param.get_value())
227                                 new_index = (old_index + direction + len(keys))%len(keys)
228                                 type_param.set_value(keys[new_index])
229                                 changed = True
230                         except: pass
231                 return changed
232
233         def port_controller_modify(self, direction):
234                 """
235                 Change the port controller.
236                 @param direction +1 or -1
237                 @return true for change
238                 """
239                 return False
240
241         ##############################################
242         ## Import/Export Methods
243         ##############################################
244         def export_data(self):
245                 """
246                 Export this block's params to nested data.
247                 @return a nested data odict
248                 """
249                 n = odict()
250                 n['key'] = self.get_key()
251                 n['param'] = map(lambda p: p.export_data(), self.get_params())
252                 return n
253
254         def import_data(self, n):
255                 """
256                 Import this block's params from nested data.
257                 Any param keys that do not exist will be ignored.
258                 @param n the nested data odict
259                 """
260                 params_n = n.findall('param')
261                 for param_n in params_n:
262                         key = param_n.find('key')
263                         value = param_n.find('value')
264                         #the key must exist in this block's params
265                         if key in self.get_param_keys():
266                                 self.get_param(key).set_value(value)