]> git.gag.com Git - debian/gnuradio/blob - grc/src/platforms/base/FlowGraph.py
fixed bug: use title option, name removed
[debian/gnuradio] / grc / src / platforms / base / FlowGraph.py
1 """
2 Copyright 2008 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 utils
21 from ... utils import odict
22 from Element import Element
23 from Block import Block
24 from Connection import Connection
25 from ... gui import Messages
26
27 class FlowGraph(Element):
28
29         def __init__(self, platform):
30                 """
31                 Make a flow graph from the arguments.
32                 @param platform a platforms with blocks and contrcutors
33                 @return the flow graph object
34                 """
35                 #hold connections and blocks
36                 self._elements = list()
37                 #initialize
38                 Element.__init__(self, platform)
39                 #inital blank import
40                 self.import_data({'flow_graph': {}})
41
42         def _get_unique_id(self, base_id=''):
43                 """
44                 Get a unique id starting with the base id.
45                 @param base_id the id starts with this and appends a count
46                 @return a unique id
47                 """
48                 index = 0
49                 while True:
50                         id = '%s_%d'%(base_id, index)
51                         index = index + 1
52                         #make sure that the id is not used by another block
53                         if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id
54
55         def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id'))
56
57         def get_option(self, key):
58                 """
59                 Get the option for a given key.
60                 The option comes from the special options block.
61                 @param key the param key for the options block
62                 @return the value held by that param
63                 """
64                 return self._options_block.get_param(key).evaluate()
65
66         def is_flow_graph(self): return True
67
68         ##############################################
69         ## Access Elements
70         ##############################################
71         def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
72         def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
73         def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
74         def get_elements(self):
75                 """
76                 Get a list of all the elements.
77                 Always ensure that the options block is in the list.
78                 @return the element list
79                 """
80                 if self._options_block not in self._elements: self._elements.append(self._options_block)
81                 #ensure uniqueness of the elements list
82                 element_set = set()
83                 element_list = list()
84                 for element in self._elements:
85                         if element not in element_set: element_list.append(element)
86                         element_set.add(element)
87                 #store cleaned up list
88                 self._elements = element_list
89                 return self._elements
90
91         def get_enabled_blocks(self):
92                 """
93                 Get a list of all blocks that are enabled.
94                 @return a list of blocks
95                 """
96                 return filter(lambda b: b.get_enabled(), self.get_blocks())
97
98         def get_enabled_connections(self):
99                 """
100                 Get a list of all connections that are enabled.
101                 @return a list of connections
102                 """
103                 return filter(lambda c: c.get_enabled(), self.get_connections())
104
105         def get_new_block(self, key):
106                 """
107                 Get a new block of the specified key.
108                 Add the block to the list of elements.
109                 @param key the block key
110                 @return the new block or None if not found
111                 """
112                 self.flag()
113                 if key not in self.get_parent().get_block_keys(): return None
114                 block = self.get_parent().get_new_block(self, key)
115                 self.get_elements().append(block)
116                 return block
117
118         def connect(self, porta, portb):
119                 """
120                 Create a connection between porta and portb.
121                 @param porta a port
122                 @param portb another port
123                 @throw Exception bad connection
124                 @return the new connection
125                 """
126                 self.flag()
127                 connection = self.get_parent().Connection(self, porta, portb)
128                 self.get_elements().append(connection)
129                 return connection
130
131         def remove_element(self, element):
132                 """
133                 Remove the element from the list of elements.
134                 If the element is a port, remove the whole block.
135                 If the element is a block, remove its connections.
136                 If the element is a connection, just remove the connection.
137                 """
138                 self.flag()
139                 if element not in self.get_elements(): return
140                 #found a port, set to parent signal block
141                 if element.is_port():
142                         element = element.get_parent()
143                 #remove block, remove all involved connections
144                 if element.is_block():
145                         for port in element.get_ports():
146                                 map(lambda c: self.remove_element(c), port.get_connections())
147                 #remove a connection
148                 elif element.is_connection(): pass
149                 self.get_elements().remove(element)
150
151         def evaluate(self, expr):
152                 """
153                 Evaluate the expression.
154                 @param expr the string expression
155                 @throw NotImplementedError
156                 """
157                 raise NotImplementedError
158
159         def validate(self):
160                 """
161                 Validate the flow graph.
162                 All connections and blocks must be valid.
163                 """
164                 for c in self.get_elements():
165                         try: assert(c.is_valid())
166                         except AssertionError: self._add_error_message('Element "%s" is not valid.'%c)
167
168         ##############################################
169         ## Import/Export Methods
170         ##############################################
171         def export_data(self):
172                 """
173                 Export this flow graph to nested data.
174                 Export all block and connection data.
175                 @return a nested data odict
176                 """
177                 import time
178                 n = odict()
179                 n['timestamp'] = time.ctime()
180                 n['block'] = [block.export_data() for block in self.get_blocks()]
181                 n['connection'] = [connection.export_data() for connection in self.get_connections()]
182                 return {'flow_graph': n}
183
184         def import_data(self, n):
185                 """
186                 Import blocks and connections into this flow graph.
187                 Clear this flowgraph of all previous blocks and connections.
188                 Any blocks or connections in error will be ignored.
189                 @param n the nested data odict
190                 """
191                 #remove previous elements
192                 self._elements = list()
193                 #the flow graph tag must exists, or use blank data
194                 if 'flow_graph' in n.keys(): fg_n = n['flow_graph']
195                 else:
196                         Messages.send_error_load('Flow graph data not found, loading blank flow graph.')
197                         fg_n = {}
198                 blocks_n = utils.listify(fg_n, 'block')
199                 connections_n = utils.listify(fg_n, 'connection')
200                 #create option block
201                 self._options_block = self.get_parent().get_new_block(self, 'options')
202                 self._options_block.get_param('id').set_value('options')
203                 #build the blocks
204                 for block_n in blocks_n:
205                         key = block_n['key']
206                         if key == 'options': block = self._options_block
207                         else: block = self.get_new_block(key)
208                         #only load the block when the block key was valid
209                         if block: block.import_data(block_n)
210                         else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
211                 #build the connections
212                 for connection_n in connections_n:
213                         #test that the data tags exist
214                         try:
215                                 assert('source_block_id' in connection_n.keys())
216                                 assert('sink_block_id' in connection_n.keys())
217                                 assert('source_key' in connection_n.keys())
218                                 assert('sink_key' in connection_n.keys())
219                         except AssertionError: continue
220                         #try to make the connection
221                         try:
222                                 #get the block ids
223                                 source_block_id = connection_n['source_block_id']
224                                 sink_block_id = connection_n['sink_block_id']
225                                 #get the port keys
226                                 source_key = connection_n['source_key']
227                                 sink_key = connection_n['sink_key']
228                                 #verify the blocks
229                                 block_ids = map(lambda b: b.get_id(), self.get_blocks())
230                                 assert(source_block_id in block_ids)
231                                 assert(sink_block_id in block_ids)
232                                 #get the blocks
233                                 source_block = self.get_block(source_block_id)
234                                 sink_block = self.get_block(sink_block_id)
235                                 #verify the ports
236                                 assert(source_key in source_block.get_source_keys())
237                                 assert(sink_key in sink_block.get_sink_keys())
238                                 #get the ports
239                                 source = source_block.get_source(source_key)
240                                 sink = sink_block.get_sink(sink_key)
241                                 #build the connection
242                                 self.connect(source, sink)
243                         except AssertionError: Messages.send_error_load('Connection between %s(%s) and %s(%s) could not be made.'%(source_block_id, source_key, sink_block_id, sink_key))
244                 self.validate()