Imported Upstream version 3.2.2
[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_elements(self):
72                 """
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
76                 """
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)
82                 return self._elements
83
84         def get_enabled_blocks(self):
85                 """
86                 Get a list of all blocks that are enabled.
87                 @return a list of blocks
88                 """
89                 return filter(lambda b: b.get_enabled(), self.get_blocks())
90
91         def get_enabled_connections(self):
92                 """
93                 Get a list of all connections that are enabled.
94                 @return a list of connections
95                 """
96                 return filter(lambda c: c.get_enabled(), self.get_connections())
97
98         def get_new_block(self, key):
99                 """
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
104                 """
105                 self.flag()
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                 self.flag()
120                 connection = self.get_parent().Connection(self, porta, portb)
121                 self.get_elements().append(connection)
122                 return connection
123
124         def remove_element(self, element):
125                 """
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.
130                 """
131                 self.flag()
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)
141
142         def evaluate(self, expr):
143                 """
144                 Evaluate the expression.
145                 @param expr the string expression
146                 @throw NotImplementedError
147                 """
148                 raise NotImplementedError
149
150         def validate(self):
151                 """
152                 Validate the flow graph.
153                 All connections and blocks must be valid.
154                 """
155                 Element.validate(self)
156                 for c in self.get_elements():
157                         try:
158                                 c.validate()
159                                 assert c.is_valid()
160                         except AssertionError: self.add_error_message('Element "%s" is not valid.'%c)
161
162         ##############################################
163         ## Import/Export Methods
164         ##############################################
165         def export_data(self):
166                 """
167                 Export this flow graph to nested data.
168                 Export all block and connection data.
169                 @return a nested data odict
170                 """
171                 import time
172                 n = odict()
173                 n['timestamp'] = time.ctime()
174                 n['block'] = [block.export_data() for block in self.get_blocks()]
175                 n['connection'] = [connection.export_data() for connection in self.get_connections()]
176                 return odict({'flow_graph': n})
177
178         def import_data(self, n=None):
179                 """
180                 Import blocks and connections into this flow graph.
181                 Clear this flowgraph of all previous blocks and connections.
182                 Any blocks or connections in error will be ignored.
183                 @param n the nested data odict
184                 """
185                 #remove previous elements
186                 self._elements = list()
187                 #use blank data if none provided
188                 fg_n = n and n.find('flow_graph') or odict()
189                 blocks_n = fg_n.findall('block')
190                 connections_n = fg_n.findall('connection')
191                 #create option block
192                 self._options_block = self.get_parent().get_new_block(self, 'options')
193                 #build the blocks
194                 for block_n in blocks_n:
195                         key = block_n.find('key')
196                         if key == 'options': block = self._options_block
197                         else: block = self.get_new_block(key)
198                         #only load the block when the block key was valid
199                         if block: block.import_data(block_n)
200                         else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
201                 self.validate() #validate all blocks before connections are made (in case of nports)
202                 #build the connections
203                 for connection_n in connections_n:
204                         #try to make the connection
205                         try:
206                                 #get the block ids
207                                 source_block_id = connection_n.find('source_block_id')
208                                 sink_block_id = connection_n.find('sink_block_id')
209                                 #get the port keys
210                                 source_key = connection_n.find('source_key')
211                                 sink_key = connection_n.find('sink_key')
212                                 #verify the blocks
213                                 block_ids = map(lambda b: b.get_id(), self.get_blocks())
214                                 assert(source_block_id in block_ids)
215                                 assert(sink_block_id in block_ids)
216                                 #get the blocks
217                                 source_block = self.get_block(source_block_id)
218                                 sink_block = self.get_block(sink_block_id)
219                                 #verify the ports
220                                 assert(source_key in source_block.get_source_keys())
221                                 assert(sink_key in sink_block.get_sink_keys())
222                                 #get the ports
223                                 source = source_block.get_source(source_key)
224                                 sink = sink_block.get_sink(sink_key)
225                                 #build the connection
226                                 self.connect(source, sink)
227                         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))