Statistics
| Branch: | Tag: | Revision:

root / gnuradio-core / src / python / gnuradio / gr / top_block.py @ 9e181064

History | View | Annotate | Download (5.9 kB)

1 c088a546 jcorgan
#
2 c088a546 jcorgan
# Copyright 2007 Free Software Foundation, Inc.
3 c088a546 jcorgan
# 
4 c088a546 jcorgan
# This file is part of GNU Radio
5 c088a546 jcorgan
# 
6 c088a546 jcorgan
# GNU Radio is free software; you can redistribute it and/or modify
7 c088a546 jcorgan
# it under the terms of the GNU General Public License as published by
8 c088a546 jcorgan
# the Free Software Foundation; either version 3, or (at your option)
9 c088a546 jcorgan
# any later version.
10 c088a546 jcorgan
# 
11 c088a546 jcorgan
# GNU Radio is distributed in the hope that it will be useful,
12 c088a546 jcorgan
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13 c088a546 jcorgan
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 c088a546 jcorgan
# GNU General Public License for more details.
15 c088a546 jcorgan
# 
16 c088a546 jcorgan
# You should have received a copy of the GNU General Public License
17 c088a546 jcorgan
# along with GNU Radio; see the file COPYING.  If not, write to
18 c088a546 jcorgan
# the Free Software Foundation, Inc., 51 Franklin Street,
19 c088a546 jcorgan
# Boston, MA 02110-1301, USA.
20 c088a546 jcorgan
# 
21 c088a546 jcorgan
22 70dd1dc6 Eric Blossom
from gnuradio_core import top_block_swig, \
23 c088a546 jcorgan
    top_block_wait_unlocked, top_block_run_unlocked
24 c088a546 jcorgan
25 2c8ea58e eb
#import gnuradio.gr.gr_threading as _threading
26 2c8ea58e eb
import gr_threading as _threading
27 2c8ea58e eb
28 2c8ea58e eb
29 2c8ea58e eb
#
30 2c8ea58e eb
# There is no problem that can't be solved with an additional
31 2c8ea58e eb
# level of indirection...
32 2c8ea58e eb
#
33 2c8ea58e eb
# This kludge allows ^C to interrupt top_block.run and top_block.wait
34 2c8ea58e eb
#
35 06d584f7 eb
# The problem that we are working around is that Python only services
36 06d584f7 eb
# signals (e.g., KeyboardInterrupt) in its main thread.  If the main
37 06d584f7 eb
# thread is blocked in our C++ version of wait, even though Python's
38 06d584f7 eb
# SIGINT handler fires, and even though there may be other python
39 06d584f7 eb
# threads running, no one will know.  Thus instead of directly waiting
40 06d584f7 eb
# in the thread that calls wait (which is likely to be the Python main
41 06d584f7 eb
# thread), we create a separate thread that does the blocking wait,
42 06d584f7 eb
# and then use the thread that called wait to do a slow poll of an
43 06d584f7 eb
# event queue.  That thread, which is executing "wait" below is
44 06d584f7 eb
# interruptable, and if it sees a KeyboardInterrupt, executes a stop
45 06d584f7 eb
# on the top_block, then goes back to waiting for it to complete.
46 06d584f7 eb
# This ensures that the unlocked wait that was in progress (in the
47 06d584f7 eb
# _top_block_waiter thread) can complete, release its mutex and back
48 06d584f7 eb
# out.  If we don't do that, we are never able to clean up, and nasty
49 06d584f7 eb
# things occur like leaving the USRP transmitter sending a carrier.
50 06d584f7 eb
#
51 06d584f7 eb
# See also top_block.wait (below), which uses this class to implement
52 06d584f7 eb
# the interruptable wait.
53 06d584f7 eb
#
54 2c8ea58e eb
class _top_block_waiter(_threading.Thread):
55 2c8ea58e eb
    def __init__(self, tb):
56 2c8ea58e eb
        _threading.Thread.__init__(self)
57 2c8ea58e eb
        self.setDaemon(1)
58 2c8ea58e eb
        self.tb = tb
59 2c8ea58e eb
        self.event = _threading.Event()
60 2c8ea58e eb
        self.start()
61 2c8ea58e eb
62 2c8ea58e eb
    def run(self):
63 2c8ea58e eb
        top_block_wait_unlocked(self.tb)
64 2c8ea58e eb
        self.event.set()
65 2c8ea58e eb
66 2c8ea58e eb
    def wait(self):
67 06d584f7 eb
        try:
68 06d584f7 eb
            while not self.event.isSet():
69 06d584f7 eb
                self.event.wait(0.100)
70 06d584f7 eb
        except KeyboardInterrupt:
71 06d584f7 eb
            self.tb.stop()
72 06d584f7 eb
            self.wait()
73 2c8ea58e eb
74 2c8ea58e eb
75 c088a546 jcorgan
#
76 c088a546 jcorgan
# This hack forces a 'has-a' relationship to look like an 'is-a' one.
77 c088a546 jcorgan
#
78 c088a546 jcorgan
# It allows Python classes to subclass this one, while passing through
79 c088a546 jcorgan
# method calls to the C++ class shared pointer from SWIG.
80 c088a546 jcorgan
#
81 c088a546 jcorgan
# It also allows us to intercept method calls if needed.
82 c088a546 jcorgan
#
83 c088a546 jcorgan
# This allows the 'run_locked' methods, which are defined in gr_top_block.i,
84 c088a546 jcorgan
# to release the Python global interpreter lock before calling the actual
85 c088a546 jcorgan
# method in gr_top_block
86 c088a546 jcorgan
#
87 c088a546 jcorgan
class top_block(object):
88 c088a546 jcorgan
    def __init__(self, name="top_block"):
89 c088a546 jcorgan
        self._tb = top_block_swig(name)
90 c088a546 jcorgan
91 c088a546 jcorgan
    def __getattr__(self, name):
92 9d45055a jcorgan
        if not hasattr(self, "_tb"):
93 9d45055a jcorgan
            raise RuntimeError("top_block: invalid state--did you forget to call gr.top_block.__init__ in a derived class?")
94 c088a546 jcorgan
        return getattr(self._tb, name)
95 c088a546 jcorgan
96 d5066151 jcorgan
    def start(self):
97 d5066151 jcorgan
            self._tb.start()
98 d5066151 jcorgan
        
99 d5066151 jcorgan
    def stop(self):
100 d5066151 jcorgan
            self._tb.stop()
101 d5066151 jcorgan
102 c088a546 jcorgan
    def run(self):
103 2c8ea58e eb
        self.start()
104 2c8ea58e eb
        self.wait()
105 c088a546 jcorgan
106 c088a546 jcorgan
    def wait(self):
107 2c8ea58e eb
        _top_block_waiter(self._tb).wait()
108 2c8ea58e eb
109 c088a546 jcorgan
110 c088a546 jcorgan
    # FIXME: these are duplicated from hier_block2.py; they should really be implemented
111 c088a546 jcorgan
    # in the original C++ class (gr_hier_block2), then they would all be inherited here
112 c088a546 jcorgan
113 c088a546 jcorgan
    def connect(self, *points):
114 e692e713 jcorgan
        '''connect requires one or more arguments that can be coerced to endpoints.
115 c088a546 jcorgan
        If more than two arguments are provided, they are connected together successively.
116 c088a546 jcorgan
        '''
117 e692e713 jcorgan
        if len (points) < 1:
118 e692e713 jcorgan
            raise ValueError, ("connect requires at least one endpoint; %d provided." % (len (points),))
119 e692e713 jcorgan
        else:
120 e692e713 jcorgan
            if len(points) == 1:
121 8fe7f0fe Eric Blossom
                self._tb.primitive_connect(points[0].to_basic_block())
122 e692e713 jcorgan
            else:
123 e692e713 jcorgan
                for i in range (1, len (points)):
124 e692e713 jcorgan
                    self._connect(points[i-1], points[i])
125 c088a546 jcorgan
126 c088a546 jcorgan
    def _connect(self, src, dst):
127 c088a546 jcorgan
        (src_block, src_port) = self._coerce_endpoint(src)
128 c088a546 jcorgan
        (dst_block, dst_port) = self._coerce_endpoint(dst)
129 8fe7f0fe Eric Blossom
        self._tb.primitive_connect(src_block.to_basic_block(), src_port,
130 8fe7f0fe Eric Blossom
                                   dst_block.to_basic_block(), dst_port)
131 c088a546 jcorgan
132 c088a546 jcorgan
    def _coerce_endpoint(self, endp):
133 c34cf20f Eric Blossom
        if hasattr(endp, 'to_basic_block'):
134 c088a546 jcorgan
            return (endp, 0)
135 c088a546 jcorgan
        else:
136 c088a546 jcorgan
            if hasattr(endp, "__getitem__") and len(endp) == 2:
137 c088a546 jcorgan
                return endp # Assume user put (block, port)
138 c088a546 jcorgan
            else:
139 c088a546 jcorgan
                raise ValueError("unable to coerce endpoint")
140 c088a546 jcorgan
141 c088a546 jcorgan
    def disconnect(self, *points):
142 8fe7f0fe Eric Blossom
        '''disconnect requires one or more arguments that can be coerced to endpoints.
143 c088a546 jcorgan
        If more than two arguments are provided, they are disconnected successively.
144 c088a546 jcorgan
        '''
145 e692e713 jcorgan
        if len (points) < 1:
146 8fe7f0fe Eric Blossom
            raise ValueError, ("disconnect requires at least one endpoint; %d provided." % (len (points),))
147 e692e713 jcorgan
        else:
148 e692e713 jcorgan
            if len(points) == 1:
149 8fe7f0fe Eric Blossom
                self._tb.primitive_disconnect(points[0].to_basic_block())
150 e692e713 jcorgan
            else:
151 e692e713 jcorgan
                for i in range (1, len (points)):
152 e692e713 jcorgan
                    self._disconnect(points[i-1], points[i])
153 c088a546 jcorgan
154 c088a546 jcorgan
    def _disconnect(self, src, dst):
155 c088a546 jcorgan
        (src_block, src_port) = self._coerce_endpoint(src)
156 c088a546 jcorgan
        (dst_block, dst_port) = self._coerce_endpoint(dst)
157 8fe7f0fe Eric Blossom
        self._tb.primitive_disconnect(src_block.to_basic_block(), src_port,
158 8fe7f0fe Eric Blossom
                                      dst_block.to_basic_block(), dst_port)