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
19 ##@package grc.elements.FlowGraph
20 #Primative flow graph.
24 from grc.Utils import odict
25 from grc.elements.Element import Element
26 from grc.elements.Block import Block
27 from grc.elements.Connection import Connection
29 from grc import Messages
31 class FlowGraph(Element):
33 def __init__(self, platform):
35 Make a flow graph from the arguments.
36 @param platform a platforms with blocks and contrcutors
37 @return the flow graph object
39 #hold connections and blocks
40 self._elements = list()
42 Element.__init__(self, platform)
44 self.import_data({'flow_graph': {}})
46 def __str__(self): return 'FlowGraph - "%s"'%self.get_option('name')
48 def get_option(self, key):
50 Get the option for a given key.
51 The option comes from the special options block.
52 @param key the param key for the options block
53 @return the value held by that param
55 return self._options_block.get_param(key).evaluate()
57 def is_flow_graph(self): return True
59 ##############################################
61 ##############################################
62 def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
63 def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
64 def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
65 def get_elements(self):
67 Get a list of all the elements.
68 Always ensure that the options block is in the list.
69 @return the element list
71 if self._options_block not in self._elements: self._elements.append(self._options_block)
72 #ensure uniqueness of the elements list
75 for element in self._elements:
76 if element not in element_set: element_list.append(element)
77 element_set.add(element)
78 #store cleaned up list
79 self._elements = element_list
82 def get_enabled_blocks(self):
84 Get a list of all blocks that are enabled.
85 @return a list of blocks
87 return filter(lambda b: b.get_enabled(), self.get_blocks())
89 def get_enabled_connections(self):
91 Get a list of all connections that are enabled.
92 @return a list of connections
94 return filter(lambda c: c.get_enabled(), self.get_connections())
96 def get_new_block(self, key):
98 Get a new block of the specified key.
99 Add the block to the list of elements.
100 @param key the block key
101 @return the new block or None if not found
104 if key not in self.get_parent().get_block_keys(): return None
105 block = self.get_parent().get_new_block(self, key)
106 self.get_elements().append(block)
109 def connect(self, porta, portb):
111 Create a connection between porta and portb.
113 @param portb another port
114 @throw Exception bad connection
115 @return the new connection
118 connection = self.get_parent().Connection(self, porta, portb)
119 self.get_elements().append(connection)
122 def remove_element(self, element):
124 Remove the element from the list of elements.
125 If the element is a port, remove the whole block.
126 If the element is a block, remove its connections.
127 If the element is a connection, just remove the connection.
130 if element not in self.get_elements(): return
131 #found a port, set to parent signal block
132 if element.is_port():
133 element = element.get_parent()
134 #remove block, remove all involved connections
135 if element.is_block():
136 for port in element.get_ports():
137 map(lambda c: self.remove_element(c), port.get_connections())
139 elif element.is_connection(): pass
140 self.get_elements().remove(element)
142 def evaluate(self, expr):
144 Evaluate the expression.
145 @param expr the string expression
146 @throw NotImplementedError
148 raise NotImplementedError
152 Validate the flow graph.
153 All connections and blocks must be valid.
155 for c in self.get_elements():
156 try: assert(c.is_valid())
157 except AssertionError: self._add_error_message('Element "%s" is not valid.'%c)
159 ##############################################
160 ## Import/Export Methods
161 ##############################################
162 def export_data(self):
164 Export this flow graph to nested data.
165 Export all block and connection data.
166 @return a nested data odict
170 n['timestamp'] = time.ctime()
171 n['block'] = [block.export_data() for block in self.get_blocks()]
172 n['connection'] = [connection.export_data() for connection in self.get_connections()]
173 return {'flow_graph': n}
175 def import_data(self, n):
177 Import blocks and connections into this flow graph.
178 Clear this flowgraph of all previous blocks and connections.
179 Any blocks or connections in error will be ignored.
180 @param n the nested data odict
182 #remove previous elements
183 self._elements = list()
184 #the flow graph tag must exists, or use blank data
185 if 'flow_graph' in n.keys(): fg_n = n['flow_graph']
187 Messages.send_error_load('Flow graph data not found, loading blank flow graph.')
189 blocks_n = Utils.listify(fg_n, 'block')
190 connections_n = Utils.listify(fg_n, 'connection')
192 self._options_block = self.get_parent().get_new_block(self, 'options')
193 self._options_block.get_param('id').set_value('options')
195 for block_n in blocks_n:
197 if key == 'options': block = self._options_block
198 else: block = self.get_new_block(key)
199 #only load the block when the block key was valid
200 if block: block.import_data(block_n)
201 else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
202 #build the connections
203 for connection_n in connections_n:
204 #test that the data tags exist
206 assert('source_block_id' in connection_n.keys())
207 assert('sink_block_id' in connection_n.keys())
208 assert('source_key' in connection_n.keys())
209 assert('sink_key' in connection_n.keys())
210 except AssertionError: continue
211 #try to make the connection
214 source_block_id = connection_n['source_block_id']
215 sink_block_id = connection_n['sink_block_id']
217 source_key = connection_n['source_key']
218 sink_key = connection_n['sink_key']
220 block_ids = map(lambda b: b.get_id(), self.get_blocks())
221 assert(source_block_id in block_ids)
222 assert(sink_block_id in block_ids)
224 source_block = self.get_block(source_block_id)
225 sink_block = self.get_block(sink_block_id)
227 assert(source_key in source_block.get_source_keys())
228 assert(sink_key in sink_block.get_sink_keys())
230 source = source_block.get_source(source_key)
231 sink = sink_block.get_sink(sink_key)
232 #build the connection
233 self.connect(source, sink)
234 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))