2 Copyright 2008, 2009 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 Element import Element
22 from Block import Block
23 from Connection import Connection
24 from .. gui import Messages
26 class FlowGraph(Element):
28 def __init__(self, platform):
30 Make a flow graph from the arguments.
31 @param platform a platforms with blocks and contrcutors
32 @return the flow graph object
35 Element.__init__(self, platform)
39 def _get_unique_id(self, base_id=''):
41 Get a unique id starting with the base id.
42 @param base_id the id starts with this and appends a count
47 id = '%s_%d'%(base_id, index)
49 #make sure that the id is not used by another block
50 if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id
52 def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id'))
54 def get_option(self, key):
56 Get the option for a given key.
57 The option comes from the special options block.
58 @param key the param key for the options block
59 @return the value held by that param
61 return self._options_block.get_param(key).get_evaluated()
63 def is_flow_graph(self): return True
65 ##############################################
67 ##############################################
68 def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
69 def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
70 def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
71 def get_elements(self):
73 Get a list of all the elements.
74 Always ensure that the options block is in the list (only once).
75 @return the element list
77 options_block_count = self._elements.count(self._options_block)
78 if not options_block_count:
79 self._elements.append(self._options_block)
80 for i in range(options_block_count-1):
81 self._elements.remove(self._options_block)
84 def get_enabled_blocks(self):
86 Get a list of all blocks that are enabled.
87 @return a list of blocks
89 return filter(lambda b: b.get_enabled(), self.get_blocks())
91 def get_enabled_connections(self):
93 Get a list of all connections that are enabled.
94 @return a list of connections
96 return filter(lambda c: c.get_enabled(), self.get_connections())
98 def get_new_block(self, key):
100 Get a new block of the specified key.
101 Add the block to the list of elements.
102 @param key the block key
103 @return the new block or None if not found
106 if key not in self.get_parent().get_block_keys(): return None
107 block = self.get_parent().get_new_block(self, key)
108 self.get_elements().append(block)
111 def connect(self, porta, portb):
113 Create a connection between porta and portb.
115 @param portb another port
116 @throw Exception bad connection
117 @return the new connection
120 connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb)
121 self.get_elements().append(connection)
124 def remove_element(self, element):
126 Remove the element from the list of elements.
127 If the element is a port, remove the whole block.
128 If the element is a block, remove its connections.
129 If the element is a connection, just remove the connection.
132 if element not in self.get_elements(): return
133 #found a port, set to parent signal block
134 if element.is_port():
135 element = element.get_parent()
136 #remove block, remove all involved connections
137 if element.is_block():
138 for port in element.get_ports():
139 map(self.remove_element, port.get_connections())
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 Rewrite critical structures.
153 Call rewrite on all sub elements.
155 Element.rewrite(self)
156 for elem in self.get_elements(): elem.rewrite()
160 Validate the flow graph.
161 Validate only the blocks.
162 Connections will be validated within the blocks.
164 Element.validate(self)
165 for c in self.get_blocks():
168 self.add_error_message('Element "%s" is not valid.'%c)
170 ##############################################
171 ## Import/Export Methods
172 ##############################################
173 def export_data(self):
175 Export this flow graph to nested data.
176 Export all block and connection data.
177 @return a nested data odict
181 n['timestamp'] = time.ctime()
182 n['block'] = [block.export_data() for block in self.get_blocks()]
183 n['connection'] = [connection.export_data() for connection in self.get_connections()]
184 return odict({'flow_graph': n})
186 def import_data(self, n=None):
188 Import blocks and connections into this flow graph.
189 Clear this flowgraph of all previous blocks and connections.
190 Any blocks or connections in error will be ignored.
191 @param n the nested data odict
193 #remove previous elements
194 self._elements = list()
195 #use blank data if none provided
196 fg_n = n and n.find('flow_graph') or odict()
197 blocks_n = fg_n.findall('block')
198 connections_n = fg_n.findall('connection')
200 self._options_block = self.get_parent().get_new_block(self, 'options')
202 for block_n in blocks_n:
203 key = block_n.find('key')
204 if key == 'options': block = self._options_block
205 else: block = self.get_new_block(key)
206 #only load the block when the block key was valid
207 if block: block.import_data(block_n)
208 else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
209 self.rewrite() #rewrite all blocks before connections are made (ex: nports)
210 #build the connections
211 for connection_n in connections_n:
212 #try to make the connection
215 source_block_id = connection_n.find('source_block_id')
216 sink_block_id = connection_n.find('sink_block_id')
218 source_key = connection_n.find('source_key')
219 sink_key = connection_n.find('sink_key')
221 block_ids = map(lambda b: b.get_id(), self.get_blocks())
222 assert(source_block_id in block_ids)
223 assert(sink_block_id in block_ids)
225 source_block = self.get_block(source_block_id)
226 sink_block = self.get_block(sink_block_id)
228 assert(source_key in source_block.get_source_keys())
229 assert(sink_key in sink_block.get_sink_keys())
231 source = source_block.get_source(source_key)
232 sink = sink_block.get_sink(sink_key)
233 #build the connection
234 self.connect(source, sink)
235 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))
236 self.rewrite() #global rewrite