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
105 if key not in self.get_parent().get_block_keys(): return None
106 block = self.get_parent().get_new_block(self, key)
107 self.get_elements().append(block)
110 def connect(self, porta, portb):
112 Create a connection between porta and portb.
114 @param portb another port
115 @throw Exception bad connection
116 @return the new connection
118 connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=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.
129 if element not in self.get_elements(): return
130 #found a port, set to parent signal block
131 if element.is_port():
132 element = element.get_parent()
133 #remove block, remove all involved connections
134 if element.is_block():
135 for port in element.get_ports():
136 map(self.remove_element, port.get_connections())
137 self.get_elements().remove(element)
139 def evaluate(self, expr):
141 Evaluate the expression.
142 @param expr the string expression
143 @throw NotImplementedError
145 raise NotImplementedError
149 Rewrite critical structures.
150 Call rewrite on all sub elements.
152 Element.rewrite(self)
153 for elem in self.get_elements(): elem.rewrite()
157 Validate the flow graph.
158 Validate only the blocks.
159 Connections will be validated within the blocks.
161 Element.validate(self)
162 for c in self.get_blocks():
165 self.add_error_message('Element "%s" is not valid.'%c)
167 ##############################################
168 ## Import/Export Methods
169 ##############################################
170 def export_data(self):
172 Export this flow graph to nested data.
173 Export all block and connection data.
174 @return a nested data odict
178 n['timestamp'] = time.ctime()
179 n['block'] = [block.export_data() for block in self.get_blocks()]
180 n['connection'] = [connection.export_data() for connection in self.get_connections()]
181 return odict({'flow_graph': n})
183 def import_data(self, n=None):
185 Import blocks and connections into this flow graph.
186 Clear this flowgraph of all previous blocks and connections.
187 Any blocks or connections in error will be ignored.
188 @param n the nested data odict
190 #remove previous elements
191 self._elements = list()
192 #use blank data if none provided
193 fg_n = n and n.find('flow_graph') or odict()
194 blocks_n = fg_n.findall('block')
195 connections_n = fg_n.findall('connection')
197 self._options_block = self.get_parent().get_new_block(self, 'options')
199 for block_n in blocks_n:
200 key = block_n.find('key')
201 if key == 'options': block = self._options_block
202 else: block = self.get_new_block(key)
203 #only load the block when the block key was valid
204 if block: block.import_data(block_n)
205 else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
206 self.rewrite() #rewrite all blocks before connections are made (ex: nports)
207 #build the connections
208 for connection_n in connections_n:
209 #try to make the connection
212 source_block_id = connection_n.find('source_block_id')
213 sink_block_id = connection_n.find('sink_block_id')
215 source_key = connection_n.find('source_key')
216 sink_key = connection_n.find('sink_key')
218 block_ids = map(lambda b: b.get_id(), self.get_blocks())
219 assert(source_block_id in block_ids)
220 assert(sink_block_id in block_ids)
222 source_block = self.get_block(source_block_id)
223 sink_block = self.get_block(sink_block_id)
225 assert(source_key in source_block.get_source_keys())
226 assert(sink_key in sink_block.get_sink_keys())
228 source = source_block.get_source(source_key)
229 sink = sink_block.get_sink(sink_key)
230 #build the connection
231 self.connect(source, sink)
232 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))
233 self.rewrite() #global rewrite