summaryrefslogtreecommitdiff
path: root/gnuradio-runtime/python/gnuradio/gr/top_block.py
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-runtime/python/gnuradio/gr/top_block.py')
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/top_block.py170
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)
+