Beispiel #1
0
class Plot:
    # TODO scaling of size when resizing
    # TODO tighter fit of plot
    # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes
    # Plot object used GUI
    def __init__(self, builder, GPU, maxpoints, precision, linux_kernelmain,
                 linux_kernelsub):
        # Can used for kernel specific workarounds
        self.linux_kernelmain = linux_kernelmain
        self.linux_kernelsub = linux_kernelsub

        self.precision = precision
        self.builder = builder
        self.GPU = GPU
        self.maxpoints = maxpoints
        self.fig = Figure(figsize=(1000, 150), dpi=100)
        self.fig.set_tight_layout(True)
        self.ax = self.fig.add_subplot(111)
        # enable, name, unit, mean, max, current
        self.signalstore = Gtk.ListStore(bool, str, str, str, str, str)
        self.Plotsignals = self.init_signals(self.GPU)
        self.init_treeview()
        self.update_signals()
        self.canvas = FigureCanvas(self.fig)
        self.canvas.set_size_request(1000, 150)
        self.object = self.builder.get_object("matplot")
        self.object.add(self.canvas)
        self.object.show_all()

    def init_signals(self, GPU):
        Plotsignals = []

        # Define signals with: names units min max path plotenable plotcolor parser and outputargument used from parser
        Plotsignals.append(
            Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1],
                       GPU.pstate_clock[0], "/pp_dpm_sclk", True, "#1f77b4",
                       GPU.get_current_clock, 0))
        Plotsignals.append(
            Plotsignal("GPU State", "[-]", 0, len(GPU.pstate_clock),
                       "/pp_dpm_sclk", True, "#ff7f0e", GPU.get_current_clock,
                       1))
        Plotsignals.append(
            Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1],
                       GPU.pmem_clock[0], "/pp_dpm_mclk", True, "#d62728",
                       GPU.get_current_clock, 0))
        Plotsignals.append(
            Plotsignal("MEM State", "[-]", 0, len(GPU.pmem_clock),
                       "/pp_dpm_mclk", True, "#9467bd", GPU.get_current_clock,
                       1))
        Plotsignals.append(
            Plotsignal("FAN Speed", "[RPM]", 0, 255,
                       "/hwmon/hwmon0/subsystem/hwmon0/fan1_input", True,
                       "#8c564b", GPU.read_sensor))
        Plotsignals.append(
            Plotsignal(
                "TEMP 1", "[m°C]", 0,
                GPU.read_sensor("/hwmon/hwmon0/subsystem/hwmon0/temp1_crit"),
                "/hwmon/hwmon0/subsystem/hwmon0/temp1_input", True, "#e377c2",
                GPU.read_sensor))
        Plotsignals.append(
            Plotsignal("POWER", "[µW]", 0,
                       GPU.read_sensor("/hwmon/hwmon0/power1_cap_max"),
                       "/hwmon/hwmon0/power1_average", True, "#7f7f7f",
                       GPU.read_sensor))

        # GPU busy percent only properly available in linux version 4.19+
        if (self.linux_kernelmain == 4
                and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5):
            Plotsignals.append(
                Plotsignal("GPU Usage",
                           "[-]",
                           1,
                           0,
                           "/gpu_busy_percent",
                           True,
                           "#2ca02c",
                           parser=GPU.read_sensor))

        return Plotsignals

    def init_treeview(self):
        textrenderer = Gtk.CellRendererText()
        boolrenderer = Gtk.CellRendererToggle()
        boolrenderer.connect("toggled", self.on_cell_toggled)
        self.tree = self.builder.get_object("Signal Selection")
        self.tree.append_column(
            Gtk.TreeViewColumn("Plot", boolrenderer, active=0))
        columnnames = ["Name", "Unit", "mean", "max", "current"]
        for i, column in enumerate(columnnames):
            tcolumn = Gtk.TreeViewColumn(column, textrenderer, text=i + 1)
            self.tree.append_column(tcolumn)
            #if i == 0:
            #    tcolumn.set_sort_column_id(i+1)

        for plotsignal in self.Plotsignals:
            self.signalstore.append([
                plotsignal.plotenable, plotsignal.name,
                convert_to_si(plotsignal.unit)[0], '0', '0', '0'
            ])
        self.tree.set_model(self.signalstore)

    def update_signals(self):
        # Retrieve signal and set appropriate values in signalstore to update left pane in GUI
        for i, Plotsignal in enumerate(self.Plotsignals):
            Plotsignal.retrieve_data(self.maxpoints)
            self.signalstore[i][3] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1],
                    self.precision))
            self.signalstore[i][4] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1],
                    self.precision))
            self.signalstore[i][5] = str(
                np.around(
                    convert_to_si(Plotsignal.unit,
                                  Plotsignal.get_last_value())[1],
                    self.precision))

    def on_cell_toggled(self, widget, path):
        self.signalstore[path][0] = not self.signalstore[path][0]
        self.Plotsignals[int(
            path)].plotenable = not self.Plotsignals[int(path)].plotenable
        self.update_plot()

    def update_plot(self):
        self.ax.clear()
        for Plotsignal in self.Plotsignals:
            if Plotsignal.plotenable:
                self.ax.plot(convert_to_si(Plotsignal.unit,
                                           Plotsignal.get_values())[1],
                             color=Plotsignal.plotcolor)
        #self.plots = [self.ax.plot(values, color=plotcolor) for values, plotcolor in zip(self.y.T, self.signalcolors)]
        self.ax.get_yaxis().set_visible(True)
        self.ax.get_xaxis().set_visible(True)
        self.canvas.draw()
        self.canvas.flush_events()

    def refresh(self):
        # This is run in thread
        self.update_signals()
        self.update_plot()
Beispiel #2
0
class Plot:
    # TODO scaling of size when resizing
    # TODO tighter fit of plot
    # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes
    # Plot object used GUI
    def __init__(self, builder, GPUs, maxpoints, precision, linux_kernelmain,
                 linux_kernelsub):
        # Can used for kernel specific workarounds
        self.linux_kernelmain = linux_kernelmain
        self.linux_kernelsub = linux_kernelsub

        self.precision = precision
        self.builder = builder
        self.GPUs = GPUs
        self.GPU = GPUs[0]
        self.maxpoints = maxpoints
        self.fig = Figure(figsize=(1000, 150), dpi=100, facecolor="#00000000")
        self.fig.set_tight_layout(True)
        self.ax = self.fig.add_subplot(111)
        # enable, name, unit, mean, max, current
        self.signalstore = Gtk.ListStore(bool, bool, bool, str, str, str, str,
                                         str, str, str)
        self.Plotsignals = self.init_signals(self.GPU)

        # Set top panel height in accordance to number of signals (with saturation)
        height_top_panel = len(self.Plotsignals) * 32.5
        if height_top_panel < 150:
            self.builder.get_object("MainPane").set_position(150)
        elif height_top_panel > 235:
            self.builder.get_object("MainPane").set_position(235)
        else:
            self.builder.get_object("MainPane").set_position(height_top_panel)

        self.init_treeview()
        self.update_signals()
        self.canvas = FigureCanvas(self.fig)
        self.canvas.set_size_request(1000, 150)
        self.object = self.builder.get_object("matplot")
        self.object.add(self.canvas)
        self.object.show_all()

    def change_GPU(self, cardnr):
        print(f"Changing plot to GPU {self.GPUs[cardnr].fancyname}")
        self.GPU = self.GPUs[cardnr]
        self.Plotsignals = self.init_signals(self.GPU)
        self.init_treeview()
        self.update_signals()

    def init_signals(self, GPU):
        Plotsignals = []

        # Define signals with: names units max min path plotenable plotnormalise plotcolor parser and outputargument used from parser
        if GPU.gpu_clock != 'N/A':
            Plotsignals.append(
                Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1],
                           GPU.pstate_clock[0], "/pp_dpm_sclk", True, True,
                           "#1f77b4", GPU.get_current_clock, 0))
            Plotsignals.append(
                Plotsignal("GPU State", "[-]",
                           len(GPU.pstate_clock) - 1, 0, "/pp_dpm_sclk", True,
                           True, "#ff7f0e", GPU.get_current_clock, 1))
        if GPU.mem_clock != 'N/A':
            Plotsignals.append(
                Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1],
                           GPU.pmem_clock[0], "/pp_dpm_mclk", True, True,
                           "#d62728", GPU.get_current_clock, 0))
            Plotsignals.append(
                Plotsignal("MEM State", "[-]",
                           len(GPU.pmem_clock) - 1, 0, "/pp_dpm_mclk", True,
                           True, "#9467bd", GPU.get_current_clock, 1))

        self.add_available_signal(GPU.sensors,
                                  Plotsignals,
                                  hwmonpath=GPU.hwmonpath)

        # GPU busy percent only properly available in linux version 4.19+
        if (self.linux_kernelmain == 4
                and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5):
            Plotsignals.append(
                Plotsignal("GPU Usage", "[-]", 100, 0, "/gpu_busy_percent",
                           True, True, "#2ca02c", GPU.read_sensor))
        # as final check remove signals that return None:
        checked_plotlist = []
        for i, signal in enumerate(Plotsignals):
            signal.retrieve_data(self.maxpoints)
            if signal.get_last_value() is not None:
                checked_plotlist.append(signal)
            else:
                print(
                    f"Removing {signal.name} from plotsignals, returning Nonetype"
                )

        if len(checked_plotlist) == 0:
            print("Nothing to plot! Hiding the plot pane.")
            self.builder.get_object("Plot").hide()
        return checked_plotlist

    def add_available_signal(self,
                             signals,
                             Plotsignals,
                             hwmonpath="",
                             subsystem="",
                             stop_recursion=False):
        for key, value in signals.items():
            if key in subsystem_unit_color:
                subsystem = key
                stop_recursion = False
            if "path" in value:
                if subsystem == "":
                    continue
                if any(path_sensor_to_plot in value["path"]
                       for path_sensor_to_plot in sensors_to_plot):
                    signallabel = value["path"][1:].split("_")[0]
                    signalmax = 0
                    signalmin = 0
                    signalpath = hwmonpath + value["path"]
                    if "min" in signals:
                        signalmin = signals['min']['value']
                        stop_recursion = True
                    if "max" in signals:
                        signalmax = signals['max']['value']
                        stop_recursion = True
                    if "crit" in signals:
                        signalmax = signals['crit']['value']
                        stop_recursion = True
                    if "label" in signals:
                        signallabel = signals["label"]["value"]
                        if signallabel == "vddgfx" and len(
                                self.GPU.volt_range) > 0:
                            signalmax = self.GPU.volt_range[1]
                            signalmin = 0
                        stop_recursion = True
                    if "cap" in signals:
                        signalmax = signals["cap"]["value"]
                        stop_recursion = True
                    if "pwm" in value["path"]:
                        signalmax = 255
                        signallabel = "(fan)" + signallabel
                    Plotsignals.append(
                        Plotsignal(signallabel,
                                   subsystem_unit_color[subsystem]["unit"],
                                   signalmax, signalmin, signalpath, True,
                                   True,
                                   subsystem_unit_color[subsystem]["color"],
                                   read))
            else:
                if not stop_recursion:
                    self.add_available_signal(value,
                                              Plotsignals,
                                              hwmonpath=hwmonpath,
                                              subsystem=subsystem,
                                              stop_recursion=stop_recursion)
                else:
                    continue

    def init_treeview(self):
        textrenderer = Gtk.CellRendererText()
        self.plotrenderer = Gtk.CellRendererToggle()
        self.plotrenderer.connect("toggled", self.on_plot_toggled)
        self.normaliserenderer = Gtk.CellRendererToggle()
        self.normaliserenderer.connect("toggled", self.on_normalise_toggled)
        self.tree = self.builder.get_object("Signal Selection")
        self.tree.append_column(
            Gtk.TreeViewColumn("Plot", self.plotrenderer, active=0))
        self.tree.append_column(
            Gtk.TreeViewColumn("Scale",
                               self.normaliserenderer,
                               active=1,
                               activatable=2))
        columnnames = ["Name", "Unit", "min", "mean", "max", "current"]
        for i, column in enumerate(columnnames):
            tcolumn = Gtk.TreeViewColumn(column,
                                         textrenderer,
                                         text=i + 3,
                                         foreground=9)
            self.tree.append_column(tcolumn)

        for plotsignal in self.Plotsignals:
            self.signalstore.append([
                plotsignal.plotenable, plotsignal.plotnormalise, True,
                plotsignal.name,
                convert_to_si(plotsignal.unit)[0], '0', '0', '0', '0',
                plotsignal.plotcolor
            ])
        self.tree.set_model(self.signalstore)

    def update_signals(self):
        # Retrieve signal and set appropriate values in signalstore to update left pane in GUI
        for i, Plotsignal in enumerate(self.Plotsignals):
            Plotsignal.retrieve_data(self.maxpoints)
            disable_scaling = len(
                Plotsignal.get_values()) > 3 and Plotsignal.all_equal(
                ) and Plotsignal.plotnormalise and (Plotsignal.max
                                                    == Plotsignal.min)
            self.signalstore[i][2] = not disable_scaling
            if disable_scaling:
                print(
                    f"cannot scale values of {self.signalstore[i][3]} disabling scaling"
                )
                self.on_normalise_toggled(self.normaliserenderer,
                                          i,
                                          disable_refresh=True)
                if disable_plots_if_scaling_error:
                    print(
                        f"disabling {self.signalstore[i][3]} plot since disable_plots_if_scaling_error is set"
                    )
                    self.on_plot_toggled(self.plotrenderer, i)
            self.signalstore[i][5] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_min())[1],
                    self.precision))
            self.signalstore[i][6] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1],
                    self.precision))
            self.signalstore[i][7] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1],
                    self.precision))
            self.signalstore[i][8] = str(
                np.around(
                    convert_to_si(Plotsignal.unit,
                                  Plotsignal.get_last_value())[1],
                    self.precision))

    def on_plot_toggled(self, widget, path, disable_refresh=False):
        self.signalstore[path][0] = not self.signalstore[path][0]
        self.Plotsignals[int(
            path)].plotenable = not self.Plotsignals[int(path)].plotenable
        if not disable_refresh:
            self.update_plot()

    def on_normalise_toggled(self, widget, path, disable_refresh=False):
        self.signalstore[path][1] = not self.signalstore[path][1]
        self.Plotsignals[int(path)].plotnormalise = not self.Plotsignals[int(
            path)].plotnormalise
        if not disable_refresh:
            self.update_plot()

    def update_plot(self):
        if len(self.Plotsignals) == 0:
            return
        self.ax.clear()
        for Plotsignal in self.Plotsignals:
            if Plotsignal.plotenable:
                if Plotsignal.plotnormalise:
                    data = Plotsignal.get_normalised_values() * 100
                    self.ax.plot(data, color=Plotsignal.plotcolor)
                else:
                    data = Plotsignal.get_values()
                    self.ax.plot(convert_to_si(Plotsignal.unit, data)[1],
                                 color=Plotsignal.plotcolor)

        self.ax.grid(True)
        self.ax.get_yaxis().tick_right()
        self.ax.get_yaxis().set_label_position("right")
        self.ax.get_yaxis().set_visible(True)
        self.ax.get_xaxis().set_visible(False)
        all_normalised = True
        all_same_unit = True
        unit = ""
        iter = self.signalstore.get_iter(0)
        while iter is not None:
            if self.signalstore[iter][0] == True:
                if unit == "":
                    unit = self.signalstore[iter][4]
                if self.signalstore[iter][1] == False:
                    all_normalised = False
                if self.signalstore[iter][4] != unit:
                    all_same_unit = False
            if not all_normalised and not all_same_unit:
                break
            iter = self.signalstore.iter_next(iter)
        if all_normalised:
            self.ax.set_yticks(np.arange(0, 101, step=25))
            self.ax.set_ylabel('Percent [%]')
        else:
            self.ax.yaxis.set_major_locator(AutoLocator())
            if all_same_unit:
                self.ax.set_ylabel(unit)
            else:
                self.ax.set_ylabel("")
        self.canvas.draw()
        self.canvas.flush_events()

    def refresh(self):
        # This is run in thread
        self.update_signals()
        self.update_plot()
Beispiel #3
0
class Plot:
    # TODO scaling of size when resizing
    # TODO tighter fit of plot
    # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes
    # Plot object used GUI
    def __init__(self, builder, GPU, maxpoints, precision, linux_kernelmain,
                 linux_kernelsub):
        # Can used for kernel specific workarounds
        self.linux_kernelmain = linux_kernelmain
        self.linux_kernelsub = linux_kernelsub

        self.precision = precision
        self.builder = builder
        self.GPU = GPU
        self.maxpoints = maxpoints
        self.fig = Figure(figsize=(1000, 150), dpi=100, facecolor="#00000000")
        self.fig.set_tight_layout(True)
        self.ax = self.fig.add_subplot(111)
        # enable, name, unit, mean, max, current
        self.signalstore = Gtk.ListStore(bool, bool, str, str, str, str, str,
                                         str)
        self.Plotsignals = self.init_signals(self.GPU)
        self.init_treeview()
        self.update_signals()
        self.canvas = FigureCanvas(self.fig)
        self.canvas.set_size_request(1000, 150)
        self.object = self.builder.get_object("matplot")
        self.object.add(self.canvas)
        self.object.show_all()

    def init_signals(self, GPU):
        Plotsignals = []

        # Define signals with: names units max min path plotenable plotnormalise plotcolor parser and outputargument used from parser
        Plotsignals.append(
            Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1],
                       GPU.pstate_clock[0], "/pp_dpm_sclk", True, True,
                       "#1f77b4", GPU.get_current_clock, 0))
        Plotsignals.append(
            Plotsignal("GPU State", "[-]",
                       len(GPU.pstate_clock) - 1, 0, "/pp_dpm_sclk", True,
                       True, "#ff7f0e", GPU.get_current_clock, 1))
        Plotsignals.append(
            Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1],
                       GPU.pmem_clock[0], "/pp_dpm_mclk", True, True,
                       "#d62728", GPU.get_current_clock, 0))
        Plotsignals.append(
            Plotsignal("MEM State", "[-]",
                       len(GPU.pmem_clock) - 1, 0, "/pp_dpm_mclk", True, True,
                       "#9467bd", GPU.get_current_clock, 1))
        if GPU.fanpwmsensors is not None:
            Plotsignals.append(
                Plotsignal("FAN Speed", "[0-255]",
                           GPU.fanpwmsensors.read_attribute('_max', True), 0,
                           GPU.fanpwmsensors.path, True, True, "#8c564b",
                           GPU.fanpwmsensors.read))
        if GPU.tempsensors is not None:
            Plotsignals.append(
                Plotsignal("TEMP 1", "[m°C]",
                           GPU.tempsensors.read_attribute('_crit', True), 0,
                           GPU.tempsensors.path, True, True, "#e377c2",
                           GPU.tempsensors.read))
        if GPU.powersensors is not None:
            Plotsignals.append(
                Plotsignal("POWER", "[µW]",
                           GPU.powersensors.read_attribute('_cap', True), 0,
                           GPU.powersensors.path, True, True, "#7f7f7f",
                           GPU.powersensors.read))

        # GPU busy percent only properly available in linux version 4.19+
        if (self.linux_kernelmain == 4
                and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5):
            Plotsignals.append(
                Plotsignal("GPU Usage",
                           "[-]",
                           100,
                           0,
                           "/gpu_busy_percent",
                           True,
                           True,
                           "#2ca02c",
                           parser=GPU.read_sensor))

        return Plotsignals

    def init_treeview(self):
        textrenderer = Gtk.CellRendererText()
        plotrenderer = Gtk.CellRendererToggle()
        plotrenderer.connect("toggled", self.on_plot_toggled)
        normaliserenderer = Gtk.CellRendererToggle()
        normaliserenderer.connect("toggled", self.on_normalise_toggled)
        self.tree = self.builder.get_object("Signal Selection")
        self.tree.append_column(
            Gtk.TreeViewColumn("Plot", plotrenderer, active=0))
        self.tree.append_column(
            Gtk.TreeViewColumn("Scale", normaliserenderer, active=1))
        columnnames = ["Name", "Unit", "mean", "max", "current"]
        for i, column in enumerate(columnnames):
            tcolumn = Gtk.TreeViewColumn(column,
                                         textrenderer,
                                         text=i + 2,
                                         foreground=7)
            self.tree.append_column(tcolumn)

        for plotsignal in self.Plotsignals:
            self.signalstore.append([
                plotsignal.plotenable, plotsignal.plotnormalise,
                plotsignal.name,
                convert_to_si(plotsignal.unit)[0], '0', '0', '0',
                plotsignal.plotcolor
            ])
        self.tree.set_model(self.signalstore)

    def update_signals(self):
        # Retrieve signal and set appropriate values in signalstore to update left pane in GUI
        for i, Plotsignal in enumerate(self.Plotsignals):
            Plotsignal.retrieve_data(self.maxpoints)
            self.signalstore[i][4] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1],
                    self.precision))
            self.signalstore[i][5] = str(
                np.around(
                    convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1],
                    self.precision))
            self.signalstore[i][6] = str(
                np.around(
                    convert_to_si(Plotsignal.unit,
                                  Plotsignal.get_last_value())[1],
                    self.precision))

    def on_plot_toggled(self, widget, path):
        self.signalstore[path][0] = not self.signalstore[path][0]
        self.Plotsignals[int(
            path)].plotenable = not self.Plotsignals[int(path)].plotenable
        self.update_plot()

    def on_normalise_toggled(self, widget, path):
        self.signalstore[path][1] = not self.signalstore[path][1]
        self.Plotsignals[int(path)].plotnormalise = not self.Plotsignals[int(
            path)].plotnormalise
        self.update_plot()

    def update_plot(self):
        self.ax.clear()
        for Plotsignal in self.Plotsignals:
            if Plotsignal.plotenable:
                if Plotsignal.plotnormalise:
                    data = Plotsignal.get_normalised_values()
                    self.ax.plot(data, color=Plotsignal.plotcolor)
                else:
                    data = Plotsignal.get_values()
                    self.ax.plot(convert_to_si(Plotsignal.unit, data)[1],
                                 color=Plotsignal.plotcolor)

        self.ax.grid(True)
        self.ax.get_yaxis().tick_right()
        self.ax.get_yaxis().set_visible(True)
        self.ax.get_xaxis().set_visible(False)
        all_normalised = True
        iter = self.signalstore.get_iter(0)
        while iter is not None:
            if self.signalstore[iter][1] == False:
                all_normalised = False
                break
            iter = self.signalstore.iter_next(iter)
        if all_normalised:
            self.ax.set_yticks(np.arange(0, 1.1, step=0.1))
        else:
            self.ax.yaxis.set_major_locator(AutoLocator())
        self.canvas.draw()
        self.canvas.flush_events()

    def refresh(self):
        # This is run in thread
        self.update_signals()
        self.update_plot()