diff --git a/data/com.github.stsdc.monitor.appdata.xml.in b/data/com.github.stsdc.monitor.appdata.xml.in index 6c258f0d..211c65fc 100644 --- a/data/com.github.stsdc.monitor.appdata.xml.in +++ b/data/com.github.stsdc.monitor.appdata.xml.in @@ -25,11 +25,10 @@ https://github.com/stsdc/monitor/issues - +
    -
  • Small bugfix
  • -
  • Added tooltips (Ryo Nakano)
  • +
  • System resources tab
diff --git a/debian/changelog b/debian/changelog index 05a09882..69943565 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +com.github.stsdc.monitor (0.8.0) bionic; urgency=low + + * System resources tab + +-- Stanisław Dac Sun, 19 Jul 2020 00:30:43 +0200 + com.github.stsdc.monitor (0.7.3) bionic; urgency=low * Added tooltips to process state label (Ryo Nakano) diff --git a/meson.build b/meson.build index bc29b153..294674c5 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('com.github.stsdc.monitor', 'vala', 'c', version: '0.7.3') +project('com.github.stsdc.monitor', 'vala', 'c', version: '0.8.0') # these are Meson modules gnome = import('gnome') @@ -66,6 +66,11 @@ executable( 'src/Views/ProcessView/ProcessInfoView/ProcessInfoView.vala', 'src/Views/ProcessView/ProcessTreeView/CPUProcessTreeView.vala', + 'src/Views/SystemView/SystemView.vala', + 'src/Views/SystemView/SystemCPUChart.vala', + 'src/Views/SystemView/SystemCPUView.vala', + 'src/Views/SystemView/SystemMemoryView.vala', + # Widgets related only to ProcessInfoView 'src/Views/ProcessView/ProcessInfoView/Preventor.vala', 'src/Views/ProcessView/ProcessInfoView/RoundyLabel.vala', @@ -93,8 +98,10 @@ executable( 'src/Services/Shortcuts.vala', 'src/Services/DBusServer.vala', - 'src/Services/Updater.vala', + # Resources + 'src/Resources/Resources.vala', + 'src/Resources/ResourcesSerialized.vala', 'src/Resources/CPU.vala', 'src/Resources/Core.vala', 'src/Resources/Memory.vala', @@ -125,6 +132,7 @@ shared_module( 'monitor', 'src/Indicator/Indicator.vala', 'src/Utils.vala', + 'src/Resources/ResourcesSerialized.vala', 'src/Indicator/Widgets/DisplayWidget.vala', 'src/Indicator/Widgets/PopoverWidget.vala', diff --git a/src/Indicator/Services/DBusClient.vala b/src/Indicator/Services/DBusClient.vala index 116f66cf..2f6522d7 100644 --- a/src/Indicator/Services/DBusClient.vala +++ b/src/Indicator/Services/DBusClient.vala @@ -2,7 +2,7 @@ public interface Monitor.DBusClientInterface : Object { public abstract void quit_monitor () throws Error; public abstract void show_monitor () throws Error; - public signal void update (Utils.SystemResources data); + public signal void update (ResourcesSerialized data); public signal void indicator_state (bool state); } diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 2ea83eb4..5c0c673a 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -2,18 +2,19 @@ // application reference private Shortcuts shortcuts; + private Resources resources; + // Widgets public Headerbar headerbar; // private Gtk.Button process_info_button; public ProcessView process_view; + public SystemView system_view; private Statusbar statusbar; public DBusServer dbusserver; - private Updater updater; - // Constructs a main window public MainWindow (MonitorApp app) { @@ -23,47 +24,46 @@ get_style_context ().add_class ("rounded"); + resources = new Resources (); - // button_box.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); - - // setup process info button - // process_info_button = new Gtk.Button.from_icon_name ("dialog-information-symbolic", Gtk.IconSize.LARGE_TOOLBAR); - // process_info_button.get_style_context ().remove_class ("image-button"); - // button_box.add (process_info_button); - - // setup kill process button - + process_view = new ProcessView (); + system_view = new SystemView (resources); - // TODO: Granite.Widgets.ModeButton to switch between view modes + Gtk.Stack stack = new Gtk.Stack (); + stack.set_transition_type (Gtk.StackTransitionType.SLIDE_LEFT_RIGHT); + stack.add_titled (process_view, "process_view", "Processes"); + stack.add_titled (system_view, "system_view", "System"); - // process_manager = new ProcessManager(); - process_view = new ProcessView (); + Gtk.StackSwitcher stack_switcher = new Gtk.StackSwitcher (); + stack_switcher.set_stack(stack); headerbar = new Headerbar (this); + headerbar.set_custom_title (stack_switcher); set_titlebar (headerbar); statusbar = new Statusbar (); var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); - main_box.pack_start (process_view, true, true, 0); + main_box.pack_start (stack, true, true, 0); main_box.pack_start (statusbar, false, true, 0); this.add (main_box); - updater = Updater.get_default (); dbusserver = DBusServer.get_default (); + + - updater.update.connect ((sysres) => { - statusbar.update (sysres); - dbusserver.update (sysres); - dbusserver.indicator_state (MonitorApp.settings.get_boolean ("indicator-state")); - }); - - // updating processes every 2 seconds Timeout.add_seconds (2, () => { + resources.update(); + var res = resources.serialize (); + statusbar.update (res); + dbusserver.update (res); process_view.update(); + system_view.update(); + dbusserver.indicator_state (MonitorApp.settings.get_boolean ("indicator-state")); return true; }); + dbusserver.quit.connect (() => app.quit()); dbusserver.show.connect (() => { this.deiconify (); diff --git a/src/Resources/CPU.vala b/src/Resources/CPU.vala index a3b37805..df0ded16 100644 --- a/src/Resources/CPU.vala +++ b/src/Resources/CPU.vala @@ -7,15 +7,15 @@ public class Monitor.CPU : Object { public int percentage { get { - update_percentage (); return (int)(Math.round (load * 100)); } } + public Gee.ArrayList core_list; + private double _frequency; public double frequency { get { - update_frequency (); // Convert kH to GHz return (double)(_frequency / 1000000); } @@ -24,9 +24,24 @@ public class Monitor.CPU : Object { construct { last_used = 0; last_total = 0; + + core_list = new Gee.ArrayList (); + + + debug ("Number of cores: %d", (int) get_num_processors ()); + for (int i = 0; i < (int) get_num_processors (); i++) { + var core = new Core(i); + core_list.add (core); + } } - public CPU () { + public void update () { + update_percentage(); + update_frequency(); + + foreach (var core in core_list) { + core.update(); + } } private void update_percentage () { diff --git a/src/Resources/Core.vala b/src/Resources/Core.vala index bed3b5b0..1a13113f 100644 --- a/src/Resources/Core.vala +++ b/src/Resources/Core.vala @@ -8,7 +8,7 @@ namespace Monitor { public int number { get; set; } public float percentage_used { - get { update_percentage_used (); return _percentage_used; } + get { return _percentage_used; } } public Core (int number) { @@ -17,7 +17,7 @@ namespace Monitor { last_total = 0; } - private void update_percentage_used () { + public void update () { GTop.Cpu cpu; GTop.get_cpu (out cpu); @@ -31,6 +31,8 @@ namespace Monitor { last_used = (float) used; last_total = (float) cpu.xcpu_total[number]; + + // debug("Core %d: %f%%", number, _percentage_used); } } } diff --git a/src/Resources/Memory.vala b/src/Resources/Memory.vala index b5a63a01..9ef7f4da 100644 --- a/src/Resources/Memory.vala +++ b/src/Resources/Memory.vala @@ -3,12 +3,15 @@ namespace Monitor { public class Memory : Object { public double total; public double used; + public double shared; + public double buffer; + public double cached; + public double locked; private GTop.Memory mem; public int percentage { get { - update (); return (int) (Math.round ((used / total) * 100)); } } @@ -20,10 +23,15 @@ namespace Monitor { public Memory () { } - private void update () { + public void update () { GTop.get_mem (out mem); - total = (double) (mem.total / 1024 / 1024) / 1000; - used = (double) (mem.user / 1024 / 1024) / 1000; + total = (double) (mem.total ); + used = (double) mem.user; + shared = (double) (mem.shared); + buffer = (double) (mem.buffer); + cached = (double) (mem.cached); + locked = (double) (mem.locked); + } } } diff --git a/src/Resources/Resources.vala b/src/Resources/Resources.vala new file mode 100644 index 00000000..1deef74c --- /dev/null +++ b/src/Resources/Resources.vala @@ -0,0 +1,29 @@ +public class Monitor.Resources : Object { + public CPU cpu; + public Memory memory; + public Swap swap; + + construct { + memory = new Memory (); + cpu = new CPU (); + swap = new Swap (); + } + + public void update() { + cpu.update(); + memory.update(); + } + public ResourcesSerialized serialize () { + return ResourcesSerialized () { + cpu_percentage = cpu.percentage, + cpu_frequency = cpu.frequency, + memory_percentage = memory.percentage, + memory_used = memory.used, + memory_total = memory.total, + swap_percentage = swap.percentage, + swap_used = swap.used, + swap_total = swap.total + }; + } + +} \ No newline at end of file diff --git a/src/Resources/ResourcesSerialized.vala b/src/Resources/ResourcesSerialized.vala new file mode 100644 index 00000000..019f5382 --- /dev/null +++ b/src/Resources/ResourcesSerialized.vala @@ -0,0 +1,10 @@ +public struct ResourcesSerialized { + public int cpu_percentage; + public double cpu_frequency; + public int memory_percentage; + public double memory_used; + public double memory_total; + public int swap_percentage; + public double swap_used; + public double swap_total; +} \ No newline at end of file diff --git a/src/Services/DBusServer.vala b/src/Services/DBusServer.vala index 79c35f05..d802c75b 100644 --- a/src/Services/DBusServer.vala +++ b/src/Services/DBusServer.vala @@ -9,7 +9,7 @@ public class Monitor.DBusServer : Object { return instance.once (() => { return new DBusServer (); }); } - public signal void update (Utils.SystemResources data); + public signal void update (ResourcesSerialized data); public signal void indicator_state (bool state); public signal void quit (); public signal void show (); diff --git a/src/Services/Updater.vala b/src/Services/Updater.vala deleted file mode 100644 index 9825644c..00000000 --- a/src/Services/Updater.vala +++ /dev/null @@ -1,41 +0,0 @@ -namespace Monitor { - - public class Updater : Object { - private static GLib.Once instance; - public static unowned Updater get_default () { - return instance.once (() => { return new Updater (); }); - } - - private int interval = 2; // in secs - - private CPU cpu; - private Memory memory; - private Swap swap; - private Utils.SystemResources sysres; - - public signal void update (Utils.SystemResources sysres); - - construct { - memory = new Memory (); - cpu = new CPU (); - swap = new Swap (); - - Timeout.add_seconds (interval, update_resources); - } - - private bool update_resources () { - sysres = Utils.SystemResources () { - cpu_percentage = cpu.percentage, - cpu_frequency = cpu.frequency, - memory_percentage = memory.percentage, - memory_used = memory.used, - memory_total = memory.total, - swap_percentage = swap.percentage, - swap_used = swap.used, - swap_total = swap.total - }; - update (sysres); - return true; - } - } -} diff --git a/src/Utils.vala b/src/Utils.vala index 40df65e5..4d020acf 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -1,15 +1,4 @@ namespace Monitor.Utils { - public struct SystemResources { - public int cpu_percentage; - public double cpu_frequency; - public int memory_percentage; - public double memory_used; - public double memory_total; - public int swap_percentage; - public double swap_used; - public double swap_total; - } - const string NOT_AVAILABLE = (_("N/A")); const string NO_DATA = "\u2014"; } @@ -40,22 +29,26 @@ namespace Monitor.Utils { return current_size_formatted; } - public static string int_bytes_to_human(int bytes) { - double bytes_double = (double)bytes; + public static string double_bytes_to_human(double bytes) { string units = _ ("B"); // convert to MiB if needed - if (bytes_double > 1024.0) { - bytes_double /= 1024.0; + if (bytes > 1024.0) { + bytes /= 1024.0; units = _ ("KiB"); } // convert to GiB if needed - if (bytes_double > 1024.0) { - bytes_double /= 1024.0; + if (bytes > 1024.0) { + bytes /= 1024.0; units = _ ("MiB"); } - return "%.1f %s".printf (bytes_double, units); + if (bytes > 1024.0) { + bytes /= 1024.0; + units = _ ("GiB"); + } + + return "%.1f %s".printf (bytes, units); } } diff --git a/src/Views/ProcessView/ProcessInfoView/ProcessInfoIOStats.vala b/src/Views/ProcessView/ProcessInfoView/ProcessInfoIOStats.vala index fb5905ba..c9b2bac3 100644 --- a/src/Views/ProcessView/ProcessInfoView/ProcessInfoIOStats.vala +++ b/src/Views/ProcessView/ProcessInfoView/ProcessInfoIOStats.vala @@ -57,9 +57,9 @@ public class Monitor.ProcessInfoIOStats : Gtk.Grid { } public void update (Process process) { - write_bytes_label.set_text (Utils.HumanUnitFormatter.int_bytes_to_human((int)process.io.write_bytes)); - read_bytes_label.set_text (Utils.HumanUnitFormatter.int_bytes_to_human((int)process.io.read_bytes)); - cancelled_write_bytes_label.set_text (Utils.HumanUnitFormatter.int_bytes_to_human((int)process.io.cancelled_write_bytes)); + write_bytes_label.set_text (Utils.HumanUnitFormatter.double_bytes_to_human(process.io.write_bytes)); + read_bytes_label.set_text (Utils.HumanUnitFormatter.double_bytes_to_human(process.io.read_bytes)); + cancelled_write_bytes_label.set_text (Utils.HumanUnitFormatter.double_bytes_to_human(process.io.cancelled_write_bytes)); open_files_listbox.update (process); } diff --git a/src/Views/SystemView/SystemCPUChart.vala b/src/Views/SystemView/SystemCPUChart.vala new file mode 100644 index 00000000..492112c6 --- /dev/null +++ b/src/Views/SystemView/SystemCPUChart.vala @@ -0,0 +1,71 @@ +public class Monitor.SystemCPUChart : Gtk.Box { + private LiveChart.Chart chart; + private LiveChart.Config config; + + // private Gee.ArrayList serie_list; + + + construct { + // serie_list = new Gee.ArrayList (); + + get_style_context ().add_class ("graph"); + + vexpand = true; + height_request = 120; + + config = new LiveChart.Config (); + config.y_axis.unit = "%"; + config.y_axis.tick_interval = 25; + config.y_axis.fixed_max = 100.0; + config.y_axis.labels.visible = false; + config.x_axis.labels.visible = false; + + config.padding = LiveChart.Padding () { + smart = LiveChart.AutoPadding.NONE, + top = 0, + right = 0, + bottom = 0, + left = -1 + }; + + chart = new LiveChart.Chart (config); + chart.expand = true; + chart.legend.visible = false; + chart.grid.visible = true; + chart.background.main_color = Gdk.RGBA () { + red= 1, green= 1, blue= 1, alpha= 1 + }; //White background + + + } + + public SystemCPUChart (int cores_quantity) { + for (int i = 0; i < cores_quantity; i++) { + var renderer = new LiveChart.SmoothLineArea (new LiveChart.Values(1000)); + var serie = new LiveChart.Serie ("Core x", renderer); + serie.set_main_color ({ 0.35 + i/20, 0.8, 0.1, 1.0}); + chart.add_serie (serie); + // serie_list.add (serie); + } + + add (chart); + } + + public void update (int serie_number, double value) { + // debug("%f", value); + // chart.add_value (serie_list.get(serie_number), value); + chart.add_value_by_index (serie_number, value); + } + + // public void set_data (Gee.ArrayList history) { + // var refresh_rate_is_ms = 2000; //your own refresh rate in milliseconds + // chart.add_unaware_timestamp_collection(serie_list[0], history, refresh_rate_is_ms); + // } + + public void clear () { + // var series = chart.series; + // foreach (var serie in serie_list) { + // serie.clear(); + // } + } +} diff --git a/src/Views/SystemView/SystemCPUView.vala b/src/Views/SystemView/SystemCPUView.vala new file mode 100644 index 00000000..f0d567a7 --- /dev/null +++ b/src/Views/SystemView/SystemCPUView.vala @@ -0,0 +1,70 @@ +public class Monitor.SystemCPUView : Gtk.Grid { + private SystemCPUChart cpu_chart; + private CPU cpu; + + private Gtk.Label cpu_percentage_label; + + private Gee.ArrayList core_label_list; + + construct { + margin = 12; + column_spacing = 12; + set_vexpand (false); + + core_label_list = new Gee.ArrayList (); + } + + + + public SystemCPUView(CPU _cpu) { + cpu = _cpu; + + cpu_percentage_label = new Gtk.Label ("CPU: " + Utils.NO_DATA); + cpu_percentage_label.get_style_context ().add_class ("h2"); + cpu_percentage_label.valign = Gtk.Align.START; + cpu_percentage_label.halign = Gtk.Align.START; + cpu_percentage_label.margin_start = 6; + + cpu_chart = new SystemCPUChart (cpu.core_list.size); + + attach (grid_core_labels (), 0, 0, 1); + attach (cpu_percentage_label, 1, 0, 1, 1); + attach (cpu_chart, 1, 0, 1, 2); + + } + + + public void update () { + for (int i = 0; i < cpu.core_list.size; i++) { + double core_percentage = cpu.core_list[i].percentage_used; + cpu_chart.update(i, core_percentage); + string percentage_formatted = ("% 3d%%").printf ( (int)core_percentage); + core_label_list[i].set_text ((_("Thread " + i.to_string() + ":" + percentage_formatted))); + } + + cpu_percentage_label.set_text ((_("CPU: % 3d%%")).printf (cpu.percentage)); + } + + private Gtk.Grid grid_core_labels () { + Gtk.Grid grid = new Gtk.Grid (); + grid.column_spacing = 12; + grid.width_request = 300; + int column = 0; + int row = 0; + for (int i = 0; i < cpu.core_list.size; i++) { + var core_label = new Gtk.Label("Thread " + i.to_string() + ": " + Utils.NO_DATA); + core_label.halign = Gtk.Align.START; + core_label_list.add (core_label); + + grid.attach(core_label, column, row, 1, 1); + + column++; + if (column > 1) { + row++; + column = 0; + } + } + + return grid; + } +} \ No newline at end of file diff --git a/src/Views/SystemView/SystemMemoryView.vala b/src/Views/SystemView/SystemMemoryView.vala new file mode 100644 index 00000000..25ee3b0a --- /dev/null +++ b/src/Views/SystemView/SystemMemoryView.vala @@ -0,0 +1,84 @@ +public class Monitor.SystemMemoryView : Gtk.Grid { + private SystemCPUChart memory_chart; + private Memory memory; + + private Gtk.Label memory_percentage_label; + private Gtk.Label memory_shared_label; + private Gtk.Label memory_buffered_label; + private Gtk.Label memory_cached_label; + private Gtk.Label memory_locked_label; + private Gtk.Label memory_total_label; + private Gtk.Label memory_used_label; + + construct { + margin = 12; + column_spacing = 12; + set_vexpand (false); + } + + + + public SystemMemoryView(Memory _memory) { + memory = _memory; + + memory_percentage_label = new Gtk.Label ("Memory: " + Utils.NO_DATA); + memory_percentage_label.get_style_context ().add_class ("h2"); + memory_percentage_label.halign = Gtk.Align.START; + memory_percentage_label.valign = Gtk.Align.START; + memory_percentage_label.margin_start = 6; + + memory_total_label = new Gtk.Label ("Total: " + Utils.NO_DATA); + memory_total_label.halign = Gtk.Align.START; + + memory_used_label = new Gtk.Label ("Used: " + Utils.NO_DATA); + memory_used_label.halign = Gtk.Align.START; + + memory_shared_label = new Gtk.Label ("Shared: " + Utils.NO_DATA); + memory_shared_label.halign = Gtk.Align.START; + + memory_buffered_label = new Gtk.Label ("Buffered: " + Utils.NO_DATA); + memory_buffered_label.halign = Gtk.Align.START; + + memory_cached_label = new Gtk.Label ("Cached: " + Utils.NO_DATA); + memory_cached_label.halign = Gtk.Align.START; + + memory_locked_label = new Gtk.Label ("Locked: " + Utils.NO_DATA); + memory_locked_label.halign = Gtk.Align.START; + + memory_chart = new SystemCPUChart (1); + + attach (memory_percentage_label, 1, 0, 1, 1); + attach (memory_usage_grid (), 0, 0, 1); + attach (memory_chart, 1, 0, 1, 2); + + } + + private Gtk.Grid memory_usage_grid () { + Gtk.Grid grid = new Gtk.Grid (); + grid.column_spacing = 12; + grid.width_request = 300; + + grid.attach (memory_used_label, 0, 0, 1, 1); + grid.attach (memory_total_label, 1, 0, 1, 1); + grid.attach (memory_shared_label, 0, 1, 1, 1); + grid.attach (memory_buffered_label, 1, 1, 1, 1); + grid.attach (memory_cached_label, 0, 2, 1, 1); + grid.attach (memory_locked_label, 1, 2, 1, 1); + + return grid; + } + + + public void update () { + memory_percentage_label.set_text ((_("Memory: % 3d%%")).printf (memory.percentage)); + memory_chart.update (0, memory.percentage); + + memory_total_label.set_text ((_("Total: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.total))); + memory_used_label.set_text ((_("Used: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.used))); + memory_shared_label.set_text ((_("Shared: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.shared))); + memory_buffered_label.set_text ((_("Buffered: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.buffer))); + memory_cached_label.set_text ((_("Cached: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.cached))); + memory_locked_label.set_text ((_("Locked: %s")).printf (Utils.HumanUnitFormatter.double_bytes_to_human(memory.locked))); + } + +} \ No newline at end of file diff --git a/src/Views/SystemView/SystemView.vala b/src/Views/SystemView/SystemView.vala new file mode 100644 index 00000000..162d5467 --- /dev/null +++ b/src/Views/SystemView/SystemView.vala @@ -0,0 +1,26 @@ +public class Monitor.SystemView : Gtk.Box { + private Resources resources; + + private SystemCPUView cpu_view; + private SystemMemoryView memory_view; + + construct { + orientation = Gtk.Orientation.VERTICAL; + hexpand = true; + } + + public SystemView (Resources _resources) { + resources = _resources; + + cpu_view = new SystemCPUView (resources.cpu); + memory_view = new SystemMemoryView (resources.memory); + + add (cpu_view); + add (memory_view); + } + + public void update () { + cpu_view.update(); + memory_view.update(); + } +} \ No newline at end of file diff --git a/src/Widgets/Statusbar/Statusbar.vala b/src/Widgets/Statusbar/Statusbar.vala index 2689bcc9..d30193e6 100644 --- a/src/Widgets/Statusbar/Statusbar.vala +++ b/src/Widgets/Statusbar/Statusbar.vala @@ -31,14 +31,14 @@ public class Monitor.Statusbar : Gtk.ActionBar { public Statusbar () { } - public bool update (Utils.SystemResources sysres) { + public bool update (ResourcesSerialized sysres) { cpu_usage_label.set_text (("%d%%").printf (sysres.cpu_percentage)); memory_usage_label.set_text (("%d%%").printf (sysres.memory_percentage)); string cpu_tooltip_text = ("%.2f %s").printf (sysres.cpu_frequency, _ ("GHz")); cpu_usage_label.tooltip_text = cpu_tooltip_text; - string memory_tooltip_text = ("%.1f %s / %.1f %s").printf (sysres.memory_used, _ ("GiB"), sysres.memory_total, _ ("GiB")); + string memory_tooltip_text = ("%s / %s").printf (Utils.HumanUnitFormatter.double_bytes_to_human(sysres.memory_used), Utils.HumanUnitFormatter.double_bytes_to_human(sysres.memory_total)); memory_usage_label.tooltip_text = memory_tooltip_text; // The total amount of the swap is 0 when it is unavailable diff --git a/subprojects/live-chart b/subprojects/live-chart index 34f6a173..7e1d4ca8 160000 --- a/subprojects/live-chart +++ b/subprojects/live-chart @@ -1 +1 @@ -Subproject commit 34f6a1735561358cdd4c0c598c90f01a3b948fdc +Subproject commit 7e1d4ca8e3c0fee3b88b5ff0cdd447d8001824a2