switch source package format to 3.0 quilt
[debian/gnuradio] / grc / base / FlowGraph.py
1 """
2 Copyright 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
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.
9
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.
14
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
18 """
19
20 from . import odict
21 from Element import Element
22 from .. gui import Messages
23
24 class FlowGraph(Element):
25
26         def __init__(self, platform):
27                 """
28                 Make a flow graph from the arguments.
29                 @param platform a platforms with blocks and contrcutors
30                 @return the flow graph object
31                 """
32                 #initialize
33                 Element.__init__(self, platform)
34                 #inital blank import
35                 self.import_data()
36
37         def _get_unique_id(self, base_id=''):
38                 """
39                 Get a unique id starting with the base id.
40                 @param base_id the id starts with this and appends a count
41                 @return a unique id
42                 """
43                 index = 0
44                 while True:
45                         id = '%s_%d'%(base_id, index)
46                         index = index + 1
47                         #make sure that the id is not used by another block
48                         if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id
49
50         def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id'))
51
52         def get_option(self, key):
53                 """
54                 Get the option for a given key.
55                 The option comes from the special options block.
56                 @param key the param key for the options block
57                 @return the value held by that param
58                 """
59                 return self._options_block.get_param(key).get_evaluated()
60
61         def is_flow_graph(self): return True
62
63         ##############################################
64         ## Access Elements
65         ##############################################
66         def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
67         def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
68         def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
69         def get_children(self): return self.get_elements()
70         def get_elements(self):
71                 """
72                 Get a list of all the elements.
73                 Always ensure that the options block is in the list (only once).
74                 @return the element list
75                 """
76                 options_block_count = self._elements.count(self._options_block)
77                 if not options_block_count:
78                         self._elements.append(self._options_block)
79                 for i in range(options_block_count-1):
80                         self._elements.remove(self._options_block)
81                 return self._elements
82
83         def get_enabled_blocks(self):
84                 """
85                 Get a list of all blocks that are enabled.
86                 @return a list of blocks
87                 """
88                 return filter(lambda b: b.get_enabled(), self.get_blocks())
89
90         def get_enabled_connections(self):
91                 """
92                 Get a list of all connections that are enabled.
93                 @return a list of connections
94                 """
95                 return filter(lambda c: c.get_enabled(), self.get_connections())
96
97         def get_new_block(self, key):
98                 """
99                 Get a new block of the specified key.
100                 Add the block to the list of elements.
101                 @param key the block key
102                 @return the new block or None if not found
103                 """
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)
107                 return block
108
109         def connect(self, porta, portb):
110                 """
111                 Create a connection between porta and portb.
112                 @param porta a port
113                 @param portb another port
114                 @throw Exception bad connection
115                 @return the new connection
116                 """
117                 connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb)
118                 self.get_elements().append(connection)
119                 return connection
120
121         def remove_element(self, element):
122                 """
123                 Remove the element from the list of elements.
124                 If the element is a port, remove the whole block.
125                 If the element is a block, remove its connections.
126                 If the element is a connection, just remove the connection.
127                 """
128                 if element not in self.get_elements(): return
129                 #found a port, set to parent signal block
130                 if element.is_port():
131                         element = element.get_parent()
132                 #remove block, remove all involved connections
133                 if element.is_block():
134                         for port in element.get_ports():
135                                 map(self.remove_element, port.get_connections())
136                 self.get_elements().remove(element)
137
138         def evaluate(self, expr):
139                 """
140                 Evaluate the expression.
141                 @param expr the string expression
142                 @throw NotImplementedError
143                 """
144                 raise NotImplementedError
145
146         ##############################################
147         ## Import/Export Methods
148         ##############################################
149         def export_data(self):
150                 """
151                 Export this flow graph to nested data.
152                 Export all block and connection data.
153                 @return a nested data odict
154                 """
155                 import time
156                 n = odict()
157                 n['timestamp'] = time.ctime()
158                 n['block'] = [block.export_data() for block in self.get_blocks()]
159                 n['connection'] = [connection.export_data() for connection in self.get_connections()]
160                 return odict({'flow_graph': n})
161
162         def import_data(self, n=None):
163                 """
164                 Import blocks and connections into this flow graph.
165                 Clear this flowgraph of all previous blocks and connections.
166                 Any blocks or connections in error will be ignored.
167                 @param n the nested data odict
168                 """
169                 #remove previous elements
170                 self._elements = list()
171                 #use blank data if none provided
172                 fg_n = n and n.find('flow_graph') or odict()
173                 blocks_n = fg_n.findall('block')
174                 connections_n = fg_n.findall('connection')
175                 #create option block
176                 self._options_block = self.get_parent().get_new_block(self, 'options')
177                 #build the blocks
178                 for block_n in blocks_n:
179                         key = block_n.find('key')
180                         if key == 'options': block = self._options_block
181                         else: block = self.get_new_block(key)
182                         #only load the block when the block key was valid
183                         if block: block.import_data(block_n)
184                         else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
185                 #build the connections
186                 for connection_n in connections_n:
187                         #try to make the connection
188                         try:
189                                 #get the block ids
190                                 source_block_id = connection_n.find('source_block_id')
191                                 sink_block_id = connection_n.find('sink_block_id')
192                                 #get the port keys
193                                 source_key = connection_n.find('source_key')
194                                 sink_key = connection_n.find('sink_key')
195                                 #verify the blocks
196                                 block_ids = map(lambda b: b.get_id(), self.get_blocks())
197                                 assert(source_block_id in block_ids)
198                                 assert(sink_block_id in block_ids)
199                                 #get the blocks
200                                 source_block = self.get_block(source_block_id)
201                                 sink_block = self.get_block(sink_block_id)
202                                 #verify the ports
203                                 assert(source_key in source_block.get_source_keys())
204                                 assert(sink_key in sink_block.get_sink_keys())
205                                 #get the ports
206                                 source = source_block.get_source(source_key)
207                                 sink = sink_block.get_sink(sink_key)
208                                 #build the connection
209                                 self.connect(source, sink)
210                         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))
211                 self.rewrite() #global rewrite