Added virtual sink and logic to clone port.
[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                 Validate the params, ports, and the connections to this block.
149                 """
150                 Element.validate(self)
151                 for c in self.get_params() + self.get_ports() + self.get_connections():
152                         c.validate()
153                         if not c.is_valid():
154                                 for msg in c.get_error_messages():
155                                         self.add_error_message('>>> %s:\n\t%s'%(c, msg))
156
157         def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key())
158
159         def get_id(self): return self.get_param('id').get_value()
160         def is_block(self): return True
161         def get_name(self): return self._name
162         def get_key(self): return self._key
163         def get_category(self): return self._category
164         def get_doc(self): return ''
165         def get_ports(self): return self.get_sources() + self.get_sinks()
166         def get_block_wrapper_path(self): return self._block_wrapper_path
167
168         ##############################################
169         # Access Params
170         ##############################################
171         def get_param_keys(self): return _get_keys(self._params)
172         def get_param(self, key): return _get_elem(self._params, key)
173         def get_params(self): return self._params
174
175         ##############################################
176         # Access Sinks
177         ##############################################
178         def get_sink_keys(self): return _get_keys(self._sinks)
179         def get_sink(self, key): return _get_elem(self._sinks, key)
180         def get_sinks(self): return self._sinks
181
182         ##############################################
183         # Access Sources
184         ##############################################
185         def get_source_keys(self): return _get_keys(self._sources)
186         def get_source(self, key): return _get_elem(self._sources, key)
187         def get_sources(self): return self._sources
188
189         def get_connections(self):
190                 return sum([port.get_connections() for port in self.get_ports()], [])
191
192         def resolve_dependencies(self, tmpl):
193                 """
194                 Resolve a paramater dependency with cheetah templates.
195                 @param tmpl the string with dependencies
196                 @return the resolved value
197                 """
198                 tmpl = str(tmpl)
199                 if '$' not in tmpl: return tmpl
200                 n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params())
201                 try: return str(Template(tmpl, n))
202                 except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl)
203
204         ##############################################
205         # Controller Modify
206         ##############################################
207         def type_controller_modify(self, direction):
208                 """
209                 Change the type controller.
210                 @param direction +1 or -1
211                 @return true for change
212                 """
213                 changed = False
214                 type_param = None
215                 for param in filter(lambda p: p.is_enum(), self.get_params()):
216                         children = self.get_ports() + self.get_params()
217                         #priority to the type controller
218                         if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
219                         #use param if type param is unset
220                         if not type_param: type_param = param
221                 if type_param:
222                         #try to increment the enum by direction
223                         try:
224                                 keys = type_param.get_option_keys()
225                                 old_index = keys.index(type_param.get_value())
226                                 new_index = (old_index + direction + len(keys))%len(keys)
227                                 type_param.set_value(keys[new_index])
228                                 changed = True
229                         except: pass
230                 return changed
231
232         def port_controller_modify(self, direction):
233                 """
234                 Change the port controller.
235                 @param direction +1 or -1
236                 @return true for change
237                 """
238                 return False
239
240         ##############################################
241         ## Import/Export Methods
242         ##############################################
243         def export_data(self):
244                 """
245                 Export this block's params to nested data.
246                 @return a nested data odict
247                 """
248                 n = odict()
249                 n['key'] = self.get_key()
250                 n['param'] = map(lambda p: p.export_data(), self.get_params())
251                 return n
252
253         def import_data(self, n):
254                 """
255                 Import this block's params from nested data.
256                 Any param keys that do not exist will be ignored.
257                 @param n the nested data odict
258                 """
259                 params_n = n.findall('param')
260                 for param_n in params_n:
261                         key = param_n.find('key')
262                         value = param_n.find('value')
263                         #the key must exist in this block's params
264                         if key in self.get_param_keys():
265                                 self.get_param(key).set_value(value)