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
20 from ... utils import odict
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).evaluate()
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(self, porta, 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 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 odict({'flow_graph': n})
175 def import_data(self, n=None):
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 #use blank data if none provided
185 fg_n = n and n.find('flow_graph') or odict()
186 blocks_n = fg_n.findall('block')
187 connections_n = fg_n.findall('connection')
189 self._options_block = self.get_parent().get_new_block(self, 'options')
191 for block_n in blocks_n:
192 key = block_n.find('key')
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 #try to make the connection
203 source_block_id = connection_n.find('source_block_id')
204 sink_block_id = connection_n.find('sink_block_id')
206 source_key = connection_n.find('source_key')
207 sink_key = connection_n.find('sink_key')
209 block_ids = map(lambda b: b.get_id(), self.get_blocks())
210 assert(source_block_id in block_ids)
211 assert(sink_block_id in block_ids)
213 source_block = self.get_block(source_block_id)
214 sink_block = self.get_block(sink_block_id)
216 assert(source_key in source_block.get_source_keys())
217 assert(sink_key in sink_block.get_sink_keys())
219 source = source_block.get_source(source_key)
220 sink = sink_block.get_sink(sink_key)
221 #build the connection
222 self.connect(source, sink)
223 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))