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) |