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