diff options
Diffstat (limited to 'gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx')
-rw-r--r-- | gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx new file mode 100644 index 0000000000..23e11d4174 --- /dev/null +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx @@ -0,0 +1,856 @@ +#!/usr/bin/env python +# +# Copyright 2012-2013 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. +# + +import random,math,operator +import networkx as nx +import matplotlib +matplotlib.use("QT4Agg") +import matplotlib.pyplot as plt +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar +from matplotlib.figure import Figure + +from PyQt4 import QtCore,Qt,Qwt5 +import PyQt4.QtGui as QtGui +import sys, time, re, pprint +import itertools + +from gnuradio import gr, ctrlport +from gnuradio.ctrlport.GrDataPlotter import * + +class MAINWindow(QtGui.QMainWindow): + def minimumSizeHint(self): + return QtGui.QSize(800,600) + + def __init__(self, radioclient): + + super(MAINWindow, self).__init__() + self.radioclient = radioclient + self.conns = [] + self.plots = [] + self.knobprops = [] + + self.mdiArea = QtGui.QMdiArea() + self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setCentralWidget(self.mdiArea) + + self.mdiArea.subWindowActivated.connect(self.updateMenus) + self.windowMapper = QtCore.QSignalMapper(self) + self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) + + self.createActions() + self.createMenus() + self.createToolBars() + self.createStatusBar() + self.updateMenus() + + self.setWindowTitle("GNU Radio Performance Monitor") + self.setUnifiedTitleAndToolBarOnMac(True) + + self.newCon(radioclient) + + icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" ) + self.setWindowIcon(icon) + + def newSubWindow(self, window, title): + child = window; + child.setWindowTitle(title) + self.mdiArea.addSubWindow(child) + self.conns.append(child) + child.show(); + self.mdiArea.currentSubWindow().showMaximized() + + def newCon(self, csomeBool): + child = MForm(self.radioclient, len(self.conns), self, dialogprompt = not csomeBool) + if(child.radioclient is not None): + child.setWindowTitle(str(child.radioclient)) + self.mdiArea.addSubWindow(child) + self.mdiArea.currentSubWindow().showMaximized() + + self.conns.append(child) + self.plots.append([]) + + def update(self, knobs, uid): + #sys.stderr.write("KNOB KEYS: {0}\n".format(knobs.keys())) + for plot in self.plots[uid]: + data = knobs[plot.name()].value + plot.update(data) + plot.stop() + plot.wait() + plot.start() + + def setActiveSubWindow(self, window): + if window: + self.mdiArea.setActiveSubWindow(window) + + + def createActions(self): + self.newConAct = QtGui.QAction("&New Connection", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newCon) + + self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", + statusTip="Exit the application", + triggered=QtGui.qApp.closeAllWindows) + + self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4", + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) + + self.closeAllAct = QtGui.QAction("Close &All", self, + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T); + self.tileAct = QtGui.QAction("&Tile", self, + statusTip="Tile the windows", + triggered=self.mdiArea.tileSubWindows, + shortcut=qks) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C); + self.cascadeAct = QtGui.QAction("&Cascade", self, + statusTip="Cascade the windows", shortcut=qks, + triggered=self.mdiArea.cascadeSubWindows) + + self.nextAct = QtGui.QAction("Ne&xt", self, + shortcut=QtGui.QKeySequence.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) + + self.previousAct = QtGui.QAction("Pre&vious", self, + shortcut=QtGui.QKeySequence.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) + + self.separatorAct = QtGui.QAction(self) + self.separatorAct.setSeparator(True) + + self.aboutAct = QtGui.QAction("&About", self, + statusTip="Show the application's About box", + triggered=self.about) + + self.aboutQtAct = QtGui.QAction("About &Qt", self, + statusTip="Show the Qt library's About box", + triggered=QtGui.qApp.aboutQt) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.newConAct) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.exitAct) + + self.windowMenu = self.menuBar().addMenu("&Window") + self.updateWindowMenu() + self.windowMenu.aboutToShow.connect(self.updateWindowMenu) + + self.menuBar().addSeparator() + + self.helpMenu = self.menuBar().addMenu("&Help") + self.helpMenu.addAction(self.aboutAct) + self.helpMenu.addAction(self.aboutQtAct) + + def createToolBars(self): + self.fileToolBar = self.addToolBar("File") + self.fileToolBar.addAction(self.newConAct) + + self.fileToolBar = self.addToolBar("Window") + self.fileToolBar.addAction(self.tileAct) + self.fileToolBar.addAction(self.cascadeAct) + + def createStatusBar(self): + self.statusBar().showMessage("Ready") + + + def activeMdiChild(self): + activeSubWindow = self.mdiArea.activeSubWindow() + if activeSubWindow: + return activeSubWindow.widget() + return None + + def updateMenus(self): + hasMdiChild = (self.activeMdiChild() is not None) + self.closeAct.setEnabled(hasMdiChild) + self.closeAllAct.setEnabled(hasMdiChild) + self.tileAct.setEnabled(hasMdiChild) + self.cascadeAct.setEnabled(hasMdiChild) + self.nextAct.setEnabled(hasMdiChild) + self.previousAct.setEnabled(hasMdiChild) + self.separatorAct.setVisible(hasMdiChild) + + def updateWindowMenu(self): + self.windowMenu.clear() + self.windowMenu.addAction(self.closeAct) + self.windowMenu.addAction(self.closeAllAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.tileAct) + self.windowMenu.addAction(self.cascadeAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.nextAct) + self.windowMenu.addAction(self.previousAct) + self.windowMenu.addAction(self.separatorAct) + + def about(self): + about_info = \ +'''Copyright 2012 Free Software Foundation, Inc.\n +This program is part of GNU Radio.\n +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.\n +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.\n +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.''' + + QtGui.QMessageBox.about(None, "gr-perf-monitorx", about_info) + + +class ConInfoDialog(QtGui.QDialog): + def __init__(self, parent=None): + super(ConInfoDialog, self).__init__(parent) + + self.gridLayout = QtGui.QGridLayout(self) + + + self.host = QtGui.QLineEdit(self); + self.port = QtGui.QLineEdit(self); + self.host.setText("localhost"); + self.port.setText("43243"); + + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | + QtGui.QDialogButtonBox.Cancel) + + self.gridLayout.addWidget(self.host); + self.gridLayout.addWidget(self.port); + self.gridLayout.addWidget(self.buttonBox); + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + + def accept(self): + self.done(1); + + def reject(self): + self.done(0); + + +class DataTable(QtGui.QWidget): + def update(self): + print "update" + + def __init__(self, radioclient, G): + QtGui.QWidget.__init__( self) + + self.layout = QtGui.QVBoxLayout(self); + self.hlayout = QtGui.QHBoxLayout(); + self.layout.addLayout(self.hlayout); + + self.G = G; + self.radioclient = radioclient; + + self._keymap = None + + # Create a combobox to set the type of statistic we want. + self._statistic = "Instantaneous" + self._statistics_table = {"Instantaneous": "", + "Average": "avg ", + "Variance": "var "} + self.stattype = QtGui.QComboBox() + self.stattype.addItem("Instantaneous") + self.stattype.addItem("Average") + self.stattype.addItem("Variance") + self.stattype.setMaximumWidth(200) + self.hlayout.addWidget(self.stattype); + self.stattype.currentIndexChanged.connect(self.stat_changed) + + # Create a checkbox to toggle sorting of graphs + self._sort = False + self.checksort = QtGui.QCheckBox("Sort") + self.checksort.setCheckState(self._sort) + self.hlayout.addWidget(self.checksort); + self.checksort.stateChanged.connect(self.checksort_changed) + + # set up table + self.perfTable = Qt.QTableWidget(); + self.perfTable.setColumnCount(2) + self.perfTable.verticalHeader().hide(); + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Runtime"] ); + self.perfTable.horizontalHeader().setStretchLastSection(True); + self.perfTable.setSortingEnabled(True) + nodes = self.G.nodes(data=True) + + # set up plot + self.f = plt.figure(figsize=(10,8), dpi=90) + self.sp = self.f.add_subplot(111); + self.sp.autoscale_view(True,True,True); + self.sp.set_autoscale_on(True) + self.canvas = FigureCanvas(self.f) + + # set up tabs + self.tabber = QtGui.QTabWidget(); + self.layout.addWidget(self.tabber); + self.tabber.addTab(self.perfTable,"Table View"); + self.tabber.addTab(self.canvas, "Graph View"); + + # set up timer + self.timer = QtCore.QTimer() + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.timer.start(500) + + for i in range(0,len(nodes)): + self.perfTable.setItem( + i,0, + Qt.QTableWidgetItem(nodes[i][0])) + + def table_update(self,data): + for k in data.keys(): + weight = data[k] + existing = self.perfTable.findItems(str(k),QtCore.Qt.MatchFixedString) + if(len(existing) == 0): + i = self.perfTable.rowCount(); + self.perfTable.setRowCount( i+1) + self.perfTable.setItem( i,0, Qt.QTableWidgetItem(str(k))) + self.perfTable.setItem( i,1, Qt.QTableWidgetItem(str(weight))) + else: + self.perfTable.setItem( self.perfTable.row(existing[0]),1, Qt.QTableWidgetItem(str(weight))) + + def stat_changed(self, index): + self._statistic = str(self.stattype.currentText()) + + def checksort_changed(self, state): + self._sort = state > 0 + +class DataTableBuffers(DataTable): + def __init__(self, radioclient, G): + super(DataTableBuffers, self).__init__(radioclient, G) + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Buffer Full"] ); + + def update(self): + nodes = self.G.nodes(); + + # get buffer fullness for all blocks + kl = map(lambda x: "%s::%soutput %% full" % \ + (x, self._statistics_table[self._statistic]), + nodes); + buf_knobs = self.radioclient.getKnobs(kl) + + # strip values out of ctrlport response + buffer_fullness = dict(zip( + map(lambda x: x.split("::")[0], buf_knobs.keys()), + map(lambda x: x.value, buf_knobs.values()))) + + blockport_fullness = {} + for blk in buffer_fullness: + bdata = buffer_fullness[blk] + if bdata: + for port in range(0,len(bdata)): + blockport_fullness["%s:%d"%(blk,port)] = bdata[port]; + + self.table_update(blockport_fullness); + + if(self._sort): + sorted_fullness = sorted(blockport_fullness.iteritems(), key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_fullness) + else: + if self._keymap: + sorted_fullness = len(self._keymap)*['',] + for b in blockport_fullness: + sorted_fullness[self._keymap.index(b)] = (b, blockport_fullness[b]) + else: + sorted_fullness = blockport_fullness.items() + + self.sp.clear(); + self.sp.bar(range(0,len(sorted_fullness)), map(lambda x: x[1], sorted_fullness), + alpha=0.5) + self.sp.set_ylabel("% Buffers Full"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_fullness)))) + self.sp.set_xticklabels( map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), + rotation="vertical", verticalalignment="bottom" ) + self.canvas.draw() + self.canvas.show() + +class DataTableRuntimes(DataTable): + def __init__(self, radioclient, G): + super(DataTableRuntimes, self).__init__( radioclient, G) + #self.perfTable.setRowCount(len( self.G.nodes() )) + + def update(self): + nodes = self.G.nodes(); + + # get work time for all blocks + kl = map(lambda x: "%s::%swork time" % \ + (x, self._statistics_table[self._statistic]), + nodes); + wrk_knobs = self.radioclient.getKnobs(kl) + + # strip values out of ctrlport response + total_work = sum(map(lambda x: x.value, wrk_knobs.values())) + if(total_work == 0): + total_work = 1 + work_times = dict(zip( + map(lambda x: x.split("::")[0], wrk_knobs.keys()), + map(lambda x: x.value/total_work, wrk_knobs.values()))) + + # update table view + self.table_update(work_times) + + if(self._sort): + sorted_work = sorted(work_times.iteritems(), key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_work) + else: + if self._keymap: + sorted_work = len(self._keymap)*['',] + for b in work_times: + sorted_work[self._keymap.index(b)] = (b, work_times[b]) + else: + sorted_work = work_times.items() + + self.sp.clear(); + plt.figure(self.f.number) + plt.subplot(111); + self.sp.bar(range(0,len(sorted_work)), map(lambda x: x[1], sorted_work), + alpha=0.5) + self.sp.set_ylabel("% Runtime"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_work)))) + self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), + rotation="vertical", verticalalignment="bottom" ) + + self.canvas.draw(); + self.canvas.show(); + +class MForm(QtGui.QWidget): + def update(self): + try: + try: + # update current clock type + self.prevent_clock_change = True; + kl1 = None; + if(self.clockKey == None): + kl1 = self.radioclient.getRe([".*perfcounter_clock"]) + else: + kl1 = self.radioclient.getKnobs([self.clockKey]) + self.clockKey = kl1.keys()[0] + self.currClock = kl1[self.clockKey].value + self.clockSelIdx = self.clocks.values().index(self.currClock) + self.clockSel.setCurrentIndex(self.clockSelIdx) + self.prevent_clock_change = False + except: + print "WARNING: Failed to get current clock setting!" + + nodes_stream = self.G_stream.nodes() + nodes_msg = self.G_msg.nodes() + + # get current buffer depths of all output buffers + kl = map(lambda x: "%s::%soutput %% full" % \ + (x, self._statistics_table[self._statistic]), + nodes_stream); + + st = time.time() + buf_knobs = self.radioclient.getKnobs(kl) + td1 = time.time() - st; + + # strip values out of ctrlport response + buf_vals = dict(zip( + map(lambda x: x.split("::")[0], buf_knobs.keys()), + map(lambda x: x.value, buf_knobs.values()))) + + # get work time for all blocks + kl = map(lambda x: "%s::%swork time" % \ + (x, self._statistics_table[self._statistic]), + nodes_stream); + st = time.time() + wrk_knobs = self.radioclient.getKnobs(kl) + td2 = time.time() - st; + + # strip values out of ctrlport response + total_work = sum(map(lambda x: x.value, wrk_knobs.values())) + if(total_work == 0): + total_work = 1 + work_times = dict(zip( + map(lambda x: x.split("::")[0], wrk_knobs.keys()), + map(lambda x: x.value/total_work, wrk_knobs.values()))) + work_times_padded = dict(zip( + self.G.nodes(), + [0.1]*len(self.G.nodes()))) + work_times_padded.update(work_times) + + for n in nodes_stream: + # ne is the list of edges away from this node! + ne = self.G.edges([n],True); + #for e in ne: # iterate over edges from this block + for e in ne: # iterate over edges from this block + # get the right output buffer/port weight for each edge + sourceport = e[2]["sourceport"]; + if(e[2]["type"] == "stream"): + newweight = buf_vals[n][sourceport] + e[2]["weight"] = newweight; + + for n in nodes_msg: + ne = self.G.edges([n],True); + for e in ne: # iterate over edges from this block + sourceport = e[2]["sourceport"]; + if(e[2]["type"] == "msg"): + newweight = 0.01; + e[2]["weight"] = newweight; + + # set updated weights + #self.node_weights = map(lambda x: 20+2000*work_times[x], nodes_stream); + self.node_weights = map(lambda x: 20+2000*work_times_padded[x], self.G.nodes()); + self.edge_weights = map(lambda x: 100.0*x[2]["weight"], self.G.edges(data=True)); + + # draw graph updates + self.updateGraph(); + + latency = td1 + td2; + self.parent.statusBar().showMessage("Current GNU Radio Control Port Query Latency: %f ms"%\ + (latency*1000)) + + except Exception, e: + sys.stderr.write("gr-perf-monitorx: radio.getKnobs threw exception ({0}).\n".format(e)) + if(type(self.parent) is MAINWindow): + # Find window of connection + remove = [] + for p in self.parent.mdiArea.subWindowList(): + if self.parent.conns[self.uid] == p.widget(): + remove.append(p) + + # Remove subwindows for connection and plots + for r in remove: + self.parent.mdiArea.removeSubWindow(r) + + # Clean up self + self.close() + else: + sys.exit(1) + return + + def rtt(self): + self.parent.newSubWindow( DataTableRuntimes(self.radioclient, self.G_stream), "Runtime Table" ); + + def bpt(self): + self.parent.newSubWindow( DataTableBuffers(self.radioclient, self.G_stream), "Buffers Table" ); + + def resetPCs(self): + knobs = [] + for b in self.blocks_list: + knobs += [self.radioclient.Knob(b + "::reset_perf_counters"),] + k = self.radioclient.setKnobs(knobs) + + def toggleFlowgraph(self): + if self.pauseFGAct.isChecked(): + self.pauseFlowgraph() + else: + self.unpauseFlowgraph() + + def pauseFlowgraph(self): + knobs = [self.radioclient.Knob(self.top_block + "::lock"), + self.radioclient.Knob(self.top_block + "::stop")] + k = self.radioclient.setKnobs(knobs) + + def unpauseFlowgraph(self): + knobs = [self.radioclient.Knob(self.top_block + "::unlock")] + k = self.radioclient.setKnobs(knobs) + + def stat_changed(self, index): + self._statistic = str(self.stattype.currentText()) + + def update_clock(self, clkidx): + if(self.prevent_clock_change): + return; + idx = self.clockSel.currentIndex(); + clk = self.clocks.values()[idx] +# print "UPDATE CLOCK!!! %d -> %d"%(idx,clk); + k = self.radioclient.getKnobs([self.clockKey]); + k[self.clockKey].value = clk; + km = {}; + km[self.clockKey] = k[self.clockKey]; + self.radioclient.setKnobs(km); + + def __init__(self, radioclient, uid=0, parent=None, dialogprompt = False): + + super(MForm, self).__init__() + self.radioclient = radioclient +# print("before radioclient.getHost()", radioclient.getHost(), radioclient.getPort(), "prompt", prompt) + if(dialogprompt or radioclient.getHost() is None or radioclient.getPort() is None): +# print("before ConInfoDialog") + askinfo = ConInfoDialog(self); + if askinfo.exec_(): + host = str(askinfo.host.text()); + port = str(askinfo.port.text()); +# print("before radioclient.newConnection host: %s port: %s"%(host,port)) + newradio = self.radioclient.newConnection(host, port) + if newradio is None: + print("Error making a %s connection to %s:%s from %s" % (radioclient.getName(), host, port, radioclient)) + else: + self.radioclient = newradio + + else: + self.radioclient = None + return + + self.uid = uid + self.parent = parent + + self.layoutTop = QtGui.QVBoxLayout(self) + self.ctlBox = QtGui.QHBoxLayout(); + self.layout = QtGui.QHBoxLayout() + + self.layoutTop.addLayout(self.ctlBox); + self.layoutTop.addLayout(self.layout); + + self.rttAct = QtGui.QAction("Runtime Table", + self, statusTip="Runtime Table", triggered=self.rtt) + self.rttBut = Qt.QToolButton() + self.rttBut.setDefaultAction(self.rttAct); + self.ctlBox.addWidget(self.rttBut); + + self.bptAct = QtGui.QAction("Buffer Table", + self, statusTip="Buffer Table", triggered=self.bpt) + self.bptBut = Qt.QToolButton() + self.bptBut.setDefaultAction(self.bptAct); + self.ctlBox.addWidget(self.bptBut); + + self.resetPCsAct = QtGui.QAction("Reset", self, + statusTip="Reset all Performance Counters", + triggered=self.resetPCs) + self.resetPCsBut = Qt.QToolButton() + self.resetPCsBut.setDefaultAction(self.resetPCsAct); + self.ctlBox.addWidget(self.resetPCsBut); + + self.pauseFGAct = QtGui.QAction("Pause", self, + statusTip="Pause the Flowgraph", + triggered=self.toggleFlowgraph) + self.pauseFGAct.setCheckable(True) + self.pauseFGBut = Qt.QToolButton() + self.pauseFGBut.setDefaultAction(self.pauseFGAct); + self.ctlBox.addWidget(self.pauseFGBut); + + self.prevent_clock_change = True; + self.clockKey = None; + self.clocks = {"MONOTONIC":1, "THREAD":3}; + self.clockSel = QtGui.QComboBox(self); + map(lambda x: self.clockSel.addItem(x), self.clocks.keys()); + self.ctlBox.addWidget(self.clockSel); + self.clockSel.currentIndexChanged.connect(self.update_clock); + self.prevent_clock_change = False; + + self._statistic = "Instantaneous" + self._statistics_table = {"Instantaneous": "", + "Average": "avg ", + "Variance": "var "} + self.stattype = QtGui.QComboBox() + self.stattype.addItem("Instantaneous") + self.stattype.addItem("Average") + self.stattype.addItem("Variance") + self.stattype.setMaximumWidth(200) + self.ctlBox.addWidget(self.stattype); + self.stattype.currentIndexChanged.connect(self.stat_changed) + +# self.setLayout(self.layout); + + self.radio = radioclient + self.knobprops = self.radio.properties([]) + self.parent.knobprops.append(self.knobprops) + + self.timer = QtCore.QTimer() + self.constupdatediv = 0 + self.tableupdatediv = 0 + plotsize=250 + + + # Set up the graph of blocks + input_name = lambda x: x+"::avg input % full" + output_name = lambda x: x+"::avg output % full" + wtime_name = lambda x: x+"::avg work time" + nout_name = lambda x: x+"::avg noutput_items" + nprod_name = lambda x: x+"::avg nproduced" + + tmplist = [] + knobs = self.radio.getKnobs([]) + edgelist = None + msgedgelist = None + for k in knobs: + propname = k.split("::") + blockname = propname[0] + keyname = propname[1] + if(keyname == "edge list"): + edgelist = knobs[k].value + self.top_block = blockname + elif(keyname == "msg edges list"): + msgedgelist = knobs[k].value + elif(blockname not in tmplist): + # only take gr_blocks (no hier_block2) + if(knobs.has_key(input_name(blockname))): + tmplist.append(blockname) + + + if not edgelist: + sys.stderr.write("Could not find list of edges from flowgraph. " + \ + "Make sure the option 'edges_list' is enabled " + \ + "in the ControlPort configuration.\n\n") + sys.exit(1) + + self.blocks_list = tmplist + edges = edgelist.split("\n")[0:-1] + msgedges = msgedgelist.split("\n")[0:-1] + + edgepairs_stream = []; + edgepairs_msg = []; + + # add stream connections + for e in edges: + _e = e.split("->") + edgepairs_stream.append( (_e[0].split(":")[0], _e[1].split(":")[0], + {"type":"stream", "sourceport":int(_e[0].split(":")[1])}) ); + + # add msg connections + for e in msgedges: + _e = e.split("->") + edgepairs_msg.append( (_e[0].split(":")[0], _e[1].split(":")[0], + {"type":"msg", "sourceport":_e[0].split(":")[1]}) ); + + self.G = nx.MultiDiGraph(); + self.G_stream = nx.MultiDiGraph(); + self.G_msg = nx.MultiDiGraph(); + + self.G.add_edges_from(edgepairs_stream); + self.G.add_edges_from(edgepairs_msg); + + self.G_stream.add_edges_from(edgepairs_stream); + self.G_msg.add_edges_from(edgepairs_msg); + + n_edges = self.G.edges(data=True); + for e in n_edges: + e[2]["weight"] = 5+random.random()*10; + + self.G.clear(); + self.G.add_edges_from(n_edges); + + + self.f = plt.figure(figsize=(10,8), dpi=90) + self.sp = self.f.add_subplot(111); + self.sp.autoscale_view(True,True,True); + self.sp.set_autoscale_on(True) + + self.canvas = FigureCanvas(self.f) + self.layout.addWidget(self.canvas); + + self.pos = nx.graphviz_layout(self.G); + #self.pos = nx.pygraphviz_layout(self.G); + #self.pos = nx.spectral_layout(self.G); + #self.pos = nx.circular_layout(self.G); + #self.pos = nx.shell_layout(self.G); + #self.pos = nx.spring_layout(self.G); + + # generate weights and plot + self.update(); + + # set up timer + self.timer = QtCore.QTimer() + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.timer.start(1000) + + # Set up mouse callback functions to move blocks around. + self._grabbed = False + self._current_block = '' + self.f.canvas.mpl_connect('button_press_event', + self.button_press) + self.f.canvas.mpl_connect('motion_notify_event', + self.mouse_move) + self.f.canvas.mpl_connect('button_release_event', + self.button_release) + + def button_press(self, event): + x, y = event.xdata, event.ydata + thrsh = 100 + + if(x is not None and y is not None): + nearby = map(lambda z: math.sqrt( math.pow(x-z[0],2) + math.pow(y-z[1],2)), self.pos.values()) + i = nearby.index(min(nearby)) + if(abs(self.pos.values()[i][0] - x) < thrsh and + abs(self.pos.values()[i][1]-y) < thrsh): + self._current_block = self.pos.keys()[i] + #print "MOVING BLOCK: ", self._current_block + #print "CUR POS: ", self.pos.values()[i] + self._grabbed = True + + def mouse_move(self, event): + if self._grabbed: + x, y = event.xdata, event.ydata + if(x is not None and y is not None): + #print "NEW POS: ", (x,y) + self.pos[self._current_block] = (x,y) + self.updateGraph(); + + def button_release(self, event): + self._grabbed = False + + + def openMenu(self, pos): + index = self.table.treeWidget.selectedIndexes() + item = self.table.treeWidget.itemFromIndex(index[0]) + itemname = str(item.text(0)) + self.parent.propertiesMenu(itemname, self.radioclient, self.uid) + + def updateGraph(self): + + self.canvas.updateGeometry() + self.sp.clear(); + plt.figure(self.f.number) + plt.subplot(111); + nx.draw(self.G, self.pos, + edge_color=self.edge_weights, + node_color='#A0CBE2', + width=map(lambda x: 3+math.log(x), self.edge_weights), + node_shape="s", + node_size=self.node_weights, + #edge_cmap=plt.cm.Blues, + edge_cmap=plt.cm.Reds, + ax=self.sp, + arrows=False + ) + nx.draw_networkx_labels(self.G, self.pos, + font_size=12) + + self.canvas.draw(); + self.canvas.show(); + + +class MyApp(object): + def __init__(self, args): + p = gr.prefs() + cp_on = p.get_bool("ControlPort", "on", None) + cp_edges = p.get_bool("ControlPort", "edges_list", None) + pcs_on = p.get_bool("PerfCounters", "on", None) + pcs_exported = p.get_bool("PerfCounters", "export", None) + if(not (pcs_on and cp_on and pcs_exported and cp_edges)): + print("Configuration has not turned on all of the appropriate ControlPort features:") + print("\t[ControlPort] on = {0}".format(cp_on)) + print("\t[ControlPort] edges_list = {0}".format(cp_edges)) + print("\t[PerfCounters] on = {0}".format(pcs_on)) + print("\t[PerfCounters] export = {0}".format(pcs_exported)) + exit(1) + + from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient + GNURadioControlPortClient(args, 'thrift', self.run, QtGui.QApplication(sys.argv).exec_) + + def run(self, client): + MAINWindow(client).show() + +MyApp(sys.argv) |