2 Copyright 2008 Free Software Foundation, Inc.
3 This file is part of GNU Radio
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.
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.
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
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
27 class FlowGraph(Element):
29 def __init__(self, platform):
31 Make a flow graph from the arguments.
32 @param platform a platforms with blocks and contrcutors
33 @return the flow graph object
35 #hold connections and blocks
36 self._elements = list()
38 Element.__init__(self, platform)
40 self.import_data({'flow_graph': {}})
42 def _get_unique_id(self, base_id=''):
44 Get a unique id starting with the base id.
45 @param base_id the id starts with this and appends a count
50 id = '%s_%d'%(base_id, index)
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
55 def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id'))
57 def get_option(self, key):
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
64 return self._options_block.get_param(key).evaluate()
66 def is_flow_graph(self): return True
68 ##############################################
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):
76 Get a list of all the elements.
77 Always ensure that the options block is in the list.
78 @return the element list
80 if self._options_block not in self._elements: self._elements.append(self._options_block)
81 #ensure uniqueness of the elements 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
91 def get_enabled_blocks(self):
93 Get a list of all blocks that are enabled.
94 @return a list of blocks
96 return filter(lambda b: b.get_enabled(), self.get_blocks())
98 def get_enabled_connections(self):
100 Get a list of all connections that are enabled.
101 @return a list of connections
103 return filter(lambda c: c.get_enabled(), self.get_connections())
105 def get_new_block(self, key):
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
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)
118 def connect(self, porta, portb):
120 Create a connection between porta and portb.
122 @param portb another port
123 @throw Exception bad connection
124 @return the new connection
127 connection = self.get_parent().Connection(self, porta, portb)
128 self.get_elements().append(connection)
131 def remove_element(self, element):
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.
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())
148 elif element.is_connection(): pass
149 self.get_elements().remove(element)
151 def evaluate(self, expr):
153 Evaluate the expression.
154 @param expr the string expression
155 @throw NotImplementedError
157 raise NotImplementedError
161 Validate the flow graph.
162 All connections and blocks must be valid.
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)
168 ##############################################
169 ## Import/Export Methods
170 ##############################################
171 def export_data(self):
173 Export this flow graph to nested data.
174 Export all block and connection data.
175 @return a nested data 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}
184 def import_data(self, n):
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
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']
196 Messages.send_error_load('Flow graph data not found, loading blank flow graph.')
198 blocks_n = utils.listify(fg_n, 'block')
199 connections_n = utils.listify(fg_n, 'connection')
201 self._options_block = self.get_parent().get_new_block(self, 'options')
202 self._options_block.get_param('id').set_value('options')
204 for block_n in blocks_n:
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
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
223 source_block_id = connection_n['source_block_id']
224 sink_block_id = connection_n['sink_block_id']
226 source_key = connection_n['source_key']
227 sink_key = connection_n['sink_key']
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)
233 source_block = self.get_block(source_block_id)
234 sink_block = self.get_block(sink_block_id)
236 assert(source_key in source_block.get_source_keys())
237 assert(sink_key in sink_block.get_sink_keys())
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))