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