diff options
Diffstat (limited to 'gnuradio-runtime/python/gnuradio/gr/top_block.py')
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/top_block.py | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/gnuradio-runtime/python/gnuradio/gr/top_block.py b/gnuradio-runtime/python/gnuradio/gr/top_block.py new file mode 100644 index 0000000000..944e95e5ae --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/gr/top_block.py @@ -0,0 +1,170 @@ +# +# Copyright 2007 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from runtime_swig import top_block_swig, \ + top_block_wait_unlocked, top_block_run_unlocked + +#import gnuradio.gr.gr_threading as _threading +import gr_threading as _threading + +# +# There is no problem that can't be solved with an additional +# level of indirection... +# +# This kludge allows ^C to interrupt top_block.run and top_block.wait +# +# The problem that we are working around is that Python only services +# signals (e.g., KeyboardInterrupt) in its main thread. If the main +# thread is blocked in our C++ version of wait, even though Python's +# SIGINT handler fires, and even though there may be other python +# threads running, no one will know. Thus instead of directly waiting +# in the thread that calls wait (which is likely to be the Python main +# thread), we create a separate thread that does the blocking wait, +# and then use the thread that called wait to do a slow poll of an +# event queue. That thread, which is executing "wait" below is +# interruptable, and if it sees a KeyboardInterrupt, executes a stop +# on the top_block, then goes back to waiting for it to complete. +# This ensures that the unlocked wait that was in progress (in the +# _top_block_waiter thread) can complete, release its mutex and back +# out. If we don't do that, we are never able to clean up, and nasty +# things occur like leaving the USRP transmitter sending a carrier. +# +# See also top_block.wait (below), which uses this class to implement +# the interruptable wait. +# +class _top_block_waiter(_threading.Thread): + def __init__(self, tb): + _threading.Thread.__init__(self) + self.setDaemon(1) + self.tb = tb + self.event = _threading.Event() + self.start() + + def run(self): + top_block_wait_unlocked(self.tb) + self.event.set() + + def wait(self): + try: + while not self.event.isSet(): + self.event.wait(0.100) + except KeyboardInterrupt: + self.tb.stop() + self.wait() + + +# +# This hack forces a 'has-a' relationship to look like an 'is-a' one. +# +# It allows Python classes to subclass this one, while passing through +# method calls to the C++ class shared pointer from SWIG. +# +# It also allows us to intercept method calls if needed. +# +# This allows the 'run_locked' methods, which are defined in gr_top_block.i, +# to release the Python global interpreter lock before calling the actual +# method in gr_top_block +# +class top_block(object): + """ + Top-level hierarchical block representing a flow-graph. + + This is a python wrapper around the C++ implementation to allow + python subclassing. + """ + def __init__(self, name="top_block"): + self._tb = top_block_swig(name) + + def __getattr__(self, name): + if not hasattr(self, "_tb"): + raise RuntimeError("top_block: invalid state--did you forget to call gr.top_block.__init__ in a derived class?") + return getattr(self._tb, name) + + def start(self, max_noutput_items=10000000): + self._tb.start(max_noutput_items) + + def stop(self): + self._tb.stop() + + def run(self, max_noutput_items=10000000): + self.start(max_noutput_items) + self.wait() + + def wait(self): + _top_block_waiter(self._tb).wait() + + + # FIXME: these are duplicated from hier_block2.py; they should really be implemented + # in the original C++ class (gr_hier_block2), then they would all be inherited here + + def connect(self, *points): + '''connect requires one or more arguments that can be coerced to endpoints. + If more than two arguments are provided, they are connected together successively. + ''' + if len (points) < 1: + raise ValueError, ("connect requires at least one endpoint; %d provided." % (len (points),)) + else: + if len(points) == 1: + self._tb.primitive_connect(points[0].to_basic_block()) + else: + for i in range (1, len (points)): + self._connect(points[i-1], points[i]) + + def msg_connect(self, src, srcport, dst, dstport): + self.primitive_msg_connect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport); + + def msg_disconnect(self, src, srcport, dst, dstport): + self.primitive_msg_disconnect(src.to_basic_block(), srcport, dst.to_basic_block(), dstport); + + def _connect(self, src, dst): + (src_block, src_port) = self._coerce_endpoint(src) + (dst_block, dst_port) = self._coerce_endpoint(dst) + self._tb.primitive_connect(src_block.to_basic_block(), src_port, + dst_block.to_basic_block(), dst_port) + + def _coerce_endpoint(self, endp): + if hasattr(endp, 'to_basic_block'): + return (endp, 0) + else: + if hasattr(endp, "__getitem__") and len(endp) == 2: + return endp # Assume user put (block, port) + else: + raise ValueError("unable to coerce endpoint") + + def disconnect(self, *points): + '''disconnect requires one or more arguments that can be coerced to endpoints. + If more than two arguments are provided, they are disconnected successively. + ''' + if len (points) < 1: + raise ValueError, ("disconnect requires at least one endpoint; %d provided." % (len (points),)) + else: + if len(points) == 1: + self._tb.primitive_disconnect(points[0].to_basic_block()) + else: + for i in range (1, len (points)): + self._disconnect(points[i-1], points[i]) + + def _disconnect(self, src, dst): + (src_block, src_port) = self._coerce_endpoint(src) + (dst_block, dst_port) = self._coerce_endpoint(dst) + self._tb.primitive_disconnect(src_block.to_basic_block(), src_port, + dst_block.to_basic_block(), dst_port) + |