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 __str__(self): return 'FlowGraph - "%s"'%self.get_option('name')
44 def get_option(self, key):
46 Get the option for a given key.
47 The option comes from the special options block.
48 @param key the param key for the options block
49 @return the value held by that param
51 return self._options_block.get_param(key).evaluate()
53 def is_flow_graph(self): return True
55 ##############################################
57 ##############################################
58 def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
59 def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
60 def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
61 def get_elements(self):
63 Get a list of all the elements.
64 Always ensure that the options block is in the list.
65 @return the element list
67 if self._options_block not in self._elements: self._elements.append(self._options_block)
68 #ensure uniqueness of the elements list
71 for element in self._elements:
72 if element not in element_set: element_list.append(element)
73 element_set.add(element)
74 #store cleaned up list
75 self._elements = element_list
78 def get_enabled_blocks(self):
80 Get a list of all blocks that are enabled.
81 @return a list of blocks
83 return filter(lambda b: b.get_enabled(), self.get_blocks())
85 def get_enabled_connections(self):
87 Get a list of all connections that are enabled.
88 @return a list of connections
90 return filter(lambda c: c.get_enabled(), self.get_connections())
92 def get_new_block(self, key):
94 Get a new block of the specified key.
95 Add the block to the list of elements.
96 @param key the block key
97 @return the new block or None if not found
100 if key not in self.get_parent().get_block_keys(): return None
101 block = self.get_parent().get_new_block(self, key)
102 self.get_elements().append(block)
105 def connect(self, porta, portb):
107 Create a connection between porta and portb.
109 @param portb another port
110 @throw Exception bad connection
111 @return the new connection
114 connection = self.get_parent().Connection(self, porta, portb)
115 self.get_elements().append(connection)
118 def remove_element(self, element):
120 Remove the element from the list of elements.
121 If the element is a port, remove the whole block.
122 If the element is a block, remove its connections.
123 If the element is a connection, just remove the connection.
126 if element not in self.get_elements(): return
127 #found a port, set to parent signal block
128 if element.is_port():
129 element = element.get_parent()
130 #remove block, remove all involved connections
131 if element.is_block():
132 for port in element.get_ports():
133 map(lambda c: self.remove_element(c), port.get_connections())
135 elif element.is_connection(): pass
136 self.get_elements().remove(element)
138 def evaluate(self, expr):
140 Evaluate the expression.
141 @param expr the string expression
142 @throw NotImplementedError
144 raise NotImplementedError
148 Validate the flow graph.
149 All connections and blocks must be valid.
151 for c in self.get_elements():
152 try: assert(c.is_valid())
153 except AssertionError: self._add_error_message('Element "%s" is not valid.'%c)
155 ##############################################
156 ## Import/Export Methods
157 ##############################################
158 def export_data(self):
160 Export this flow graph to nested data.
161 Export all block and connection data.
162 @return a nested data odict
166 n['timestamp'] = time.ctime()
167 n['block'] = [block.export_data() for block in self.get_blocks()]
168 n['connection'] = [connection.export_data() for connection in self.get_connections()]
169 return {'flow_graph': n}
171 def import_data(self, n):
173 Import blocks and connections into this flow graph.
174 Clear this flowgraph of all previous blocks and connections.
175 Any blocks or connections in error will be ignored.
176 @param n the nested data odict
178 #remove previous elements
179 self._elements = list()
180 #the flow graph tag must exists, or use blank data
181 if 'flow_graph' in n.keys(): fg_n = n['flow_graph']
183 Messages.send_error_load('Flow graph data not found, loading blank flow graph.')
185 blocks_n = utils.listify(fg_n, 'block')
186 connections_n = utils.listify(fg_n, 'connection')
188 self._options_block = self.get_parent().get_new_block(self, 'options')
189 self._options_block.get_param('id').set_value('options')
191 for block_n in blocks_n:
193 if key == 'options': block = self._options_block
194 else: block = self.get_new_block(key)
195 #only load the block when the block key was valid
196 if block: block.import_data(block_n)
197 else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
198 #build the connections
199 for connection_n in connections_n:
200 #test that the data tags exist
202 assert('source_block_id' in connection_n.keys())
203 assert('sink_block_id' in connection_n.keys())
204 assert('source_key' in connection_n.keys())
205 assert('sink_key' in connection_n.keys())
206 except AssertionError: continue
207 #try to make the connection
210 source_block_id = connection_n['source_block_id']
211 sink_block_id = connection_n['sink_block_id']
213 source_key = connection_n['source_key']
214 sink_key = connection_n['sink_key']
216 block_ids = map(lambda b: b.get_id(), self.get_blocks())
217 assert(source_block_id in block_ids)
218 assert(sink_block_id in block_ids)
220 source_block = self.get_block(source_block_id)
221 sink_block = self.get_block(sink_block_id)
223 assert(source_key in source_block.get_source_keys())
224 assert(sink_key in sink_block.get_sink_keys())
226 source = source_block.get_source(source_key)
227 sink = sink_block.get_sink(sink_key)
228 #build the connection
229 self.connect(source, sink)
230 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))