class TabWidgetClear(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.tabWidget = QTabWidget(self) self.setCentralWidget(self.tabWidget) self.editBox = QTextEdit(self) self.tabWidget.addTab(self.getSplitter(), 'Test') def getSplitter(self): splitter = QSplitter() splitter.addWidget(self.editBox) return splitter def toggle(self): self.tabWidget.clear() self.getSplitter()
class BeeRocksAnalyzerWidget(QWidget): def __init__(self, argv, parent=None): super(BeeRocksAnalyzerWidget, self).__init__(parent) self.logger = logging.getLogger(__name__) self.wait_for_start = True self.figsInfo = [] self.figsTitle = [] self.subplots = [] self.fig_ax = [] self.figCanvas = [] self.thread = None self.threadEvent = threading.Event() self.updateSig = UpdateSig() self.restartSig = UpdateSig() self.inCsv = None self.realtimeInterval = 1.0 self.realtimeWindow = 60.0 self.print_vals = False self.print_labels = False self.outCsv = None self.updatePlotRate = 100 self.realtimeWindow_start = -1.0 self.realtimeWindow_end = -1.0 self.update_start_time = False self.marker = ('*', 'D', 's', 'x', 'o', '^', 'p') self.color = ('b', 'g', 'c', 'm', 'k', 'y', 'r') self.linestyle = '-' self.next_color_idx = 0 self.general_label_color_idx = 0 # Log Data containers self.ap_mac2num = {} self.ap_mac2sta_mac = {} self.sta_mac2num = {} self.sta_num2color = {} # key = device_num : val = color_idx self.ap_num2color = {} # key = device_num : val = color_idx # key = (fig_num , subplot_num) : val = next_marker_idx self.fig_subplot_nums2marker = {(0, 0): 0} ########################### self.getCommandLineArgs(argv) self.figs_tab_widget = QTabWidget(self) vbox = QVBoxLayout() vbox.addWidget(self.figs_tab_widget) self.setLayout(vbox) self.addPlots() self.updateSig.sig.connect(self.updatePlotData) ############################ def closeEvent(self, event): try: self.updateSig.sig.disconnect() except Exception: pass try: self.restartSig.sig.disconnect() except Exception: pass self.figs_tab_widget.clear() self.figsInfo = [] self.figsTitle = [] self.subplots = [] self.fig_subplot_nums2marker = [] self.fig_ax = [] self.figCanvas = [] try: plt.close('all') except Exception: pass try: QWidget.closeEvent(self, event) except Exception: pass def getCommandLineArgs(self, argv): for i in range(0, len(argv)): arg_i = argv[i].strip() try: if arg_i.startswith("-f="): continue elif arg_i.startswith("-fig_title"): s = arg_i.split("=") fig = int(s[0][-2:-1]) sub_plot = int(s[0][-1:]) while len(self.figsTitle) - 1 < fig: self.figsTitle.append([]) while len(self.figsTitle[fig]) - 1 < sub_plot: self.figsTitle[fig].append([]) self.figsTitle[fig][sub_plot] = s[1] elif arg_i.startswith("-plot"): s = arg_i.split("=") fig = int(s[0][-2:-1]) sub_plot = int(s[0][-1:]) labels = s[1].split(",") while len(self.figsInfo) - 1 < fig: self.figsInfo.append([]) self.subplots.append([]) self.fig_ax.append([]) while len(self.figsInfo[fig]) - 1 < sub_plot: self.figsInfo[fig].append([]) self.subplots[fig].append([]) self.fig_ax[fig].append([]) for i in range(len(self.fig_ax)): self.fig_ax[i][0] = None for l in labels: self.figsInfo[fig][sub_plot].append(l) elif arg_i.startswith("-realtime="): s = arg_i.split("=") ss = s[1].split(",") self.realtimeInterval = float(ss[0]) self.realtimeWindow = float(ss[1]) elif arg_i.startswith("-print_vals"): self.print_vals = True elif arg_i.startswith("-print_labels"): self.print_labels = True elif arg_i.startswith("-autoplay"): pass elif arg_i.startswith("-output_csv"): self.outCsv = open('output.csv', 'w') elif arg_i.startswith("-map") or arg_i.startswith("-graphs"): pass else: self.logger.error( "Error, unknown argument ({:d}) --> {}".format( i, arg_i)) self.printUsage() sys.exit(0) except Exception: # TODO: too broad exception self.logger.error("Error(2), in argument ({:d}) --> {}".format( i, arg_i)) self.printUsage() sys.exit(0) def printUsage(self): usage = [ "usage: BeeRocksAnalyzer [options]\n", " -f=<log file> - input log file\n", " -plot<fig num><sub plot>=<Label 1>,<Label 1>,.. - enable and configure plots\n", " -realtime=<update interval sec>,<samples> - enable real time plot\n", " -print_vals - print log values to console\n", " -print_labels - print log labels to console\n", " -output_csv - output params to csv file\n" ] for line in usage: self.logger.info(line) def printLabels(self): self.logger.info("printLabels:") for a_name in dir(self): if a_name.endswith("_v"): self.logger.info(" {}".format(a_name)) def printVals(self): self.logger.info("printVals:\n") for a_name in dir(self): if a_name.endswith("_v") or a_name.endswith("_t"): self.logger.info(" {} : {}\n".format( a_name, str(getattr(BeeRocksAnalyzerWidget, a_name)))) def readSampleAndUpdateGraphs(self, line, param_t1, is_start, is_stop): if is_start: self.wait_for_start = False return elif is_stop: self.restartSig.sig.emit(1) return self.readSample(line, param_t1) # update start time if self.update_start_time: self.realtimeWindow_start = param_t1 self.realtimeWindow_end = self.realtimeWindow_start + self.realtimeInterval self.update_start_time = False if not self.update_start_time: if param_t1 > self.realtimeWindow_end: self.updateSig.sig.emit(0) self.threadEvent.wait(2.0) self.threadEvent.clear() self.update_start_time = True def readSample(self, line, param_t): param_n = [] param_v = [] if line.startswith("#") or len(line) < 4: return None i1 = line.find("|") if (i1 == -1): return i2 = line.find(",") try: param_m = str(line[i1 + 1:i2]) line_args = line[i2 + 1:].split(',') param_n = [] for arg in line_args: arg_val = arg.split(':') key = arg_val[0].lower().strip() param_n.append(key) if ("mac" in key) or ("bssid" in key) or ("backhaul" in key): param_v.append( arg.replace(":", "=", 1).split("=")[1].strip()) else: param_v.append(arg_val[1].strip()) except Exception as e: # TODO: too broad exception self.logger.error("Error, readSample() 1 line --> {}".format(line)) self.logger.exception(e) return if param_v is None: return param_m_val = param_m.split(':') param_m_n = param_m_val[0].strip() if param_m_n == "Name": # nw_map_update self.logger.debug("Updating network map") try: i_state = param_n.index('state') i_mac = param_n.index('mac') i_type = param_n.index('type') if not ("1" in param_v[i_type]): i_ap_mac = param_n.index('parent bssid') except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> {}, " "nw_map_update line does not contain state or mac" " or type or parent bssid".format(line)) self.logger.exception(e) return state = (param_v[i_state].split())[0] mac = param_v[i_mac] line_type = param_v[i_type] if not ("1" in line_type): ap_mac = param_v[i_ap_mac] if "2" in line_type: # IRE try: i_backhaul = param_n.index('backhaul') mac = param_v[ i_backhaul] # for IRE, the backhaul mac is the "client" mac except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> %s, nw_map_update IRE line" " does not contain a backhaul mac address".format( line)) self.logger.exception(e) return if ("2" in line_type) or ("3" in line_type): # IRE or client if state == "Disconnected": for mac_t in self.ap_mac2sta_mac: # remove sta from the previous mac addr if mac in self.ap_mac2sta_mac[mac_t]: self.ap_mac2sta_mac[mac_t].remove(mac) elif state == "Connected": if mac not in self.sta_mac2num: # new sta mac addr sta_id = -1 try: i_sta_id = param_n.index('sta_id') sta_id = int(param_v[i_sta_id]) except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> {}, nw_map_update - " "new STA line does not contain sta_id".format( line)) self.logger.exception(e) return self.sta_mac2num[mac] = sta_id self.defineLineColor('sta', self.sta_mac2num[mac]) if ap_mac not in self.ap_mac2num: # new ap mac addr # ap_id = len(self.ap_mac2num) ap_id = -1 try: i_ap_id = param_n.index('ap_id') ap_id = int(param_v[i_ap_id]) except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> %s, nw_map_update - " "new AP line does not contain ap_id\n{}". format(line, e)) return self.ap_mac2num[ap_mac] = ap_id self.defineLineColor('ap', ap_id) self.ap_mac2sta_mac[ap_mac] = [] for mac_t in self.ap_mac2sta_mac: # remove sta from the previous mac addr if mac in self.ap_mac2sta_mac[mac_t] and (mac_t != ap_mac): self.ap_mac2sta_mac[mac_t].remove(mac) if not (mac in self.ap_mac2sta_mac[ap_mac]): self.ap_mac2sta_mac[ap_mac].append(mac) elif param_m_n == "type" or param_m_n == "Type": param_m_v = int(param_m_val[1].strip()) try: i1 = param_n.index('mac') except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> {}, 'stats_update' line not contain mac address" .format(line)) self.logger.exception(e) return mac = param_v[i1] i1 += 1 if param_m_v == 1: # AP stats update if mac not in self.ap_mac2num: # new ap mac addr ap_id = -1 try: i_ap_id = param_n.index('ap_id') ap_id = int(param_v[i_ap_id]) except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> %s, nw_map_update" " - new AP line does not contain ap_id".format( line)) self.logger.exception(e) return self.ap_mac2num[mac] = ap_id self.defineLineColor('ap', ap_id) self.ap_mac2sta_mac[mac] = [] ap_id = self.ap_mac2num[mac] for j in range(i1, len(param_n)): # fill atrribute name = 'ap%d_' % self.ap_mac2num[mac] + param_n[j] val = int(param_v[j]) self.addAttr(param_t, name, val, 'ap', ap_id) elif param_m_v == 3: # Client stats update if mac not in self.sta_mac2num: # new sta mac addr sta_id = -1 try: i_sta_id = param_n.index('sta_id') sta_id = int(param_v[i_sta_id]) except Exception as e: # TODO: too broad exception self.logger.error( "readSample() --> %s, nw_map_update" " - new STA line does not contain sta_id".format( line)) self.logger.exception(e) return self.sta_mac2num[mac] = sta_id self.defineLineColor('sta', self.sta_mac2num[mac]) sta_num = self.sta_mac2num[mac] ap_num = None for ap_mac in self.ap_mac2sta_mac: # find ap connected to sta if mac in self.ap_mac2sta_mac[ap_mac]: # extract match ap_num connected to specific sta_mac ap_num = self.ap_mac2num[ap_mac] break if ap_num is None: self.logger.error( "Error, readSample() --> {}, 'client_stats_update'" " did not find sta_mac={} in self.ap_mac2sta_mac". format(line, mac)) return for j in range(i1, len(param_n)): name_pattern = 'ap%d_sta%d_' + param_n[j] name = name_pattern % (ap_num, sta_num) val = int(param_v[j]) # fill right atrribute with val self.addAttr(param_t, name, val, 'sta', sta_num) # End of readSample() def getAttrVal(self, name): return [ getattr(BeeRocksAnalyzerWidget, name + "_v"), getattr(BeeRocksAnalyzerWidget, name + "_t") ] def getValByName(self, name): try: ar_v = getattr(BeeRocksAnalyzerWidget, name + "_v") except Exception: ar_v = [] return ar_v def getTimeByName(self, name): try: ar_t = getattr(BeeRocksAnalyzerWidget, name + "_t") except Exception: ar_t = [] return ar_t def getTimeDiffSec(self, startTime): dt = datetime.now() - startTime dt_sec = dt.seconds + (dt.microseconds / 1000000.0) return dt_sec def addAttr(self, param_t, param_n, param_v, entity, entity_num): is_new_attr = False try: ar_v = getattr(BeeRocksAnalyzerWidget, param_n + "_v") ar_t = getattr(BeeRocksAnalyzerWidget, param_n + "_t") except Exception: ar_v = [] ar_t = [] setattr(BeeRocksAnalyzerWidget, param_n + "_v", ar_v) setattr(BeeRocksAnalyzerWidget, param_n + "_t", ar_t) is_new_attr = True if ar_t != []: if (param_t - ar_t[-1]) > 3: ar_v.append(None) ar_t.append(param_t - 0.1) ar_v.append(param_v) ar_t.append(param_t) if is_new_attr: self.addRemoveNewFigSubplots(ar_t, ar_v, param_n, True, entity, entity_num) def defineLineColor(self, entity, num): if entity == 'ap': self.ap_num2color[num] = self.next_color_idx elif entity == 'sta': self.sta_num2color[num] = self.next_color_idx else: self.logger.error("error defining color , entity={}", entity) return self.next_color_idx += 1 if self.next_color_idx == len(self.color): self.next_color_idx = 0 def addRemoveNewFigSubplots(self, ar_t, ar_v, target_lable, is_add, entity, entity_num): # print "addRemoveNewFigSubplots is_add=",is_add," entity=",entity," entity_num=",entity_num asterisk_signed = False for f in range(len(self.figsInfo)): for p in range(len(self.figsInfo[f])): ar_lables = self.figsInfo[f][p] for param_n in ar_lables: if param_n != target_lable: param_n_s = param_n.split("*") if len(param_n_s) == 1: continue re_pattern = '[0-9]+' re_str = str() for param_n_s_element in param_n_s: re_str += (param_n_s_element + re_pattern) re_str = re_str[:-len(re_pattern)] str_match = re.match(re_str, target_lable) if str_match is not None: target_lable_hit = ( str_match.group() == target_lable) else: target_lable_hit = False if not target_lable_hit: continue if is_add: self.figsInfo[f][p].append(target_lable) asterisk_signed = True else: self.figsInfo[f][p].remove(target_lable) for c in range(len(self.subplots[f][p])): if self.subplots[f][p][c][2] == target_lable: ax = self.subplots[f][p][c][0] handles, labels = ax.get_legend_handles_labels( ) lable_idx = labels.index(target_lable) del handles[lable_idx] del labels[lable_idx] ax.legend(handles, labels, loc='center left', prop={'size': 8}, bbox_to_anchor=(1, 0.5)) del self.subplots[f][p][c] plt.subplots_adjust(left=0.07, right=0.87, top=0.96, bottom=0.08) return elif is_add and (asterisk_signed or param_n == target_lable): # add plot_line ax = self.fig_ax[f][p] if entity_num is None: self.general_label_color_idx += 1 if self.general_label_color_idx >= len(self.color): self.general_label_color_idx = 0 color = self.color[self.general_label_color_idx] marker = self.marker[-1] else: color = self.getLineColor(entity, entity_num) if color is None: self.logger.error( "Error, color == None, param_n={}".format( param_n)) return marker = self.marker[0] if (f, p) in self.fig_subplot_nums2marker: marker = self.marker[ self.fig_subplot_nums2marker[(f, p)]] self.fig_subplot_nums2marker[(f, p)] += 1 if self.fig_subplot_nums2marker[(f, p)] == len( self.marker): self.fig_subplot_nums2marker[(f, p)] = 0 else: marker = self.marker[0] self.fig_subplot_nums2marker[(f, p)] = 1 plot_line, = ax.plot(ar_t, ar_v, color=color, marker=marker, markersize=5, linewidth=1.2, linestyle=self.linestyle, label=target_lable) ax.ticklabel_format(axis='y', style='plain', scilimits=(1, 4)) ax.set_xlim([0, 1]) self.subplots[f][p].append((ax, plot_line, param_n)) if target_lable.find("_event") != -1: ax.set_ylabel("[Event 0/1/..]") elif target_lable.endswith("_bps"): ax.set_ylabel("[bps]") elif target_lable.endswith("_load"): ax.set_ylabel("[%]") elif target_lable.endswith("bw"): ax.set_ylabel("[MHz]") elif target_lable.endswith("ch"): ax.set_ylabel("[Channel Number]") elif target_lable.endswith("bytes"): ax.set_ylabel("[Bytes/Sec]") elif target_lable.endswith("packets"): ax.set_ylabel("[Packets/Sec]") elif target_lable.endswith("retries"): ax.set_ylabel("[#Retries]") elif target_lable.endswith("rssi"): ax.set_ylabel("[dbm]") handles, labels = ax.get_legend_handles_labels() ax.legend(handles, labels, loc='center left', prop={'size': 8}, bbox_to_anchor=(1, 0.5)) plt.subplots_adjust(left=0.07, right=0.82, top=0.96, bottom=0.08) return def updatePlotData(self): if self.threadEvent.isSet(): return fig_num = 0 t_start = self.realtimeWindow_start - self.realtimeWindow if t_start < 0: t_start = 0 xlim = [ t_start, self.realtimeWindow_start + self.realtimeInterval + 1.0 ] for f in range(len(self.figsInfo)): if self.figsInfo[f] is None: continue self.deleteOldSamples(f) # clear unused values update_fig = False for p in range(len(self.figsInfo[f])): ar_lables = self.figsInfo[f][p] ymin = [] ymax = [] update_subplot = False ax = None for param_n in ar_lables: if param_n.find("*") == -1: try: ar_t = getattr(BeeRocksAnalyzerWidget, param_n + "_t") ar_v = getattr(BeeRocksAnalyzerWidget, param_n + "_v") except Exception: continue for s in range(len(self.subplots[f][p])): (ax, plot_line, param_n_tmp) = self.subplots[f][p][s] if param_n_tmp == param_n: update_subplot = True update_fig = True plot_line.set_data(ar_t, ar_v) min, max = self.getMinMax(ar_v) ymin.append(min) ymax.append(max) break if update_subplot: # update fig min max if len(ymin) > 0 and len(ymax) > 0: ax.set_xlim(xlim) ylim = [np.min(ymin) - 1, np.max(ymax) + 1] dy = (ylim[1] - ylim[0]) / 10.0 ylim[0] -= dy ylim[1] += dy ax.set_ylim(ylim) self.setYticks(ax, ylim[1]) if update_fig: (fig, canvas) = self.figCanvas[fig_num] canvas.draw() update_fig = False fig_num += 1 self.threadEvent.set() def getMinMax(self, vec): if (len(vec) == 0): return (0, 0) vmax = vmin = 0 for i in range(len(vec)): if vec[i] is None: continue if vmin > vec[i]: vmin = vec[i] if vmax < vec[i]: vmax = vec[i] return (vmin, vmax) def deleteOldSamples(self, fig): t_start = self.realtimeWindow_start - self.realtimeWindow if t_start < 0: t_start = 0 for f in range(len(self.figsInfo)): if f != fig: continue if self.figsInfo[f] is None: continue for p in range(len(self.figsInfo[f])): ar_lables = self.figsInfo[f][p] for param_n in ar_lables: try: ar_v = getattr(BeeRocksAnalyzerWidget, param_n + "_v") ar_t = getattr(BeeRocksAnalyzerWidget, param_n + "_t") idx = 0 t_start_idx = -1 while (idx < len(ar_t)): if ar_t[idx] >= t_start: t_start_idx = idx break idx += 1 # delete old elements if t_start_idx == -1: delattr(BeeRocksAnalyzerWidget, ar_v) delattr(BeeRocksAnalyzerWidget, ar_t) self.addRemoveNewFigSubplots( None, None, param_n, False, None, None) else: del ar_v[:t_start_idx] del ar_t[:t_start_idx] except Exception: pass def setYticks(self, ax, vmax): if vmax != 0: exp = math.log(abs(vmax), 10) else: exp = 1 math.copysign(exp, vmax) if exp >= 9: exp = 9 elif exp >= 6: exp = 6 elif exp >= 3: exp = 3 elif exp > -3: exp = 0 elif exp >= -3: exp = -3 elif exp >= -6: exp = -6 elif exp >= -9: exp = -9 ax.yaxis.set_major_formatter(FixedOrderFormatter(exp)) def getLineColor(self, entity, entity_num): if (entity == 'ap'): return self.color[self.ap_num2color[entity_num]] elif (entity == 'sta'): return self.color[self.sta_num2color[entity_num]] else: self.logger.error( "error getting line color - no such entity ={}".format(entity)) return None def addPlots(self): dpi = 80 w = 1200 h = 720 fig_num = 1 for f in range(len(self.figsInfo)): if self.figsInfo[f] is None: continue fig_frame = QWidget() fig = plt.figure(fig_num, figsize=(w / dpi, h / dpi), dpi=dpi) canvas = FigureCanvas(fig) canvas.setParent(fig_frame) mpl_toolbar = NavigationToolbar(canvas, fig_frame) vbox = QVBoxLayout() vbox.addWidget(canvas) vbox.addWidget(mpl_toolbar) hbox = QHBoxLayout() hbox.addLayout(vbox) fig_frame.setLayout(hbox) fig_subplot_num = len(self.figsInfo[f]) * 100 + 11 for p in range(len(self.figsInfo[f])): if self.fig_ax[f][p] is None: ax = fig.add_subplot(fig_subplot_num) if (len(self.figsTitle) > f) and (len(self.figsTitle[f]) > p): ax.set_title(self.figsTitle[f][p]) else: ax = fig.add_subplot(fig_subplot_num, sharex=self.fig_ax[f][0]) if (len(self.figsTitle) > f) and (len(self.figsTitle[f]) > p): ax.set_title(self.figsTitle[f][p]) ax.grid(True) if p == len(self.figsInfo[f]) - 1: ax.set_xlabel('Time [sec]') else: ax.get_xaxis().set_visible(False) self.fig_ax[f][p] = ax handles, labels = ax.get_legend_handles_labels() ax.legend(handles, labels, loc='center left', prop={'size': 8}, bbox_to_anchor=(1, 0.5)) plt.subplots_adjust(left=0.07, right=0.87, top=0.96, bottom=0.08) fig_subplot_num += 1 self.figCanvas.append((fig, canvas)) self.figs_tab_widget.addTab(fig_frame, "Graph %d" % (f + 1)) fig_num += 1