Ticket #2141: cpu-and-memory-resource-indicator.patch

File cpu-and-memory-resource-indicator.patch, 10.3 KB (added by m_anish, 14 years ago)

Memory and CPU status indicator for the frame patch.

  • extensions/deviceicon/Makefile.am

    From 713ceec0148807b4e4ce1e7824cae52603b19852 Mon Sep 17 00:00:00 2001
    From: anishmangal2002 <anishmangal2002@gmail.com>
    Date: Fri, 2 Jul 2010 20:45:28 +0530
    Subject: [PATCH] Add cpu and memory resource indicator to frame
    
    This patch adds an icon to the frame, whose palette
    menu displays the memory and cpu resources. For computing
    free memory, the code reads the /proc/meminfo file (thanks
    quozl) and for computing cpu usage, the code reads the
    /proc/stat file.
    
    The frame icon is updated after every 5 seconds if required.
    Similarly, the palette menu entries are updated after every
    5 seconds as well.
    
    Signed-off-by: anishmangal2002 <anishmangal2002@gmail.com>
    ---
     extensions/deviceicon/Makefile.am  |    1 +
     extensions/deviceicon/resources.py |  241 ++++++++++++++++++++++++++++++++++++
     2 files changed, 242 insertions(+), 0 deletions(-)
     create mode 100644 extensions/deviceicon/resources.py
    
    diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am
    index 8a2e765..eff368d 100644
    a b sugar_PYTHON = \ 
    44        __init__.py     \
    55        battery.py      \
    66        network.py      \
     7        resources.py    \
    78        speaker.py      \
    89        volume.py
  • new file extensions/deviceicon/resources.py

    diff --git a/extensions/deviceicon/resources.py b/extensions/deviceicon/resources.py
    new file mode 100644
    index 0000000..1d32154
    - +  
     1# Copyright (C) Anish Mangal <anishmangal2002@gmail.com>
     2#
     3# This program is free software; you can redistribute it and/or modify
     4# it under the terms of the GNU General Public License as published by
     5# the Free Software Foundation; either version 2 of the License, or
     6# (at your option) any later version.
     7#
     8# This program is distributed in the hope that it will be useful,
     9# but WITHOUT ANY WARRANTY; without even the implied warranty of
     10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11# GNU General Public License for more details.
     12#
     13# You should have received a copy of the GNU General Public License
     14# along with this program; if not, write to the Free Software
     15# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     16
     17from gettext import gettext as _
     18import logging
     19import os
     20
     21import gobject
     22import gtk
     23import gconf
     24
     25from sugar.graphics.tray import TrayIcon
     26from sugar.graphics.xocolor import XoColor
     27from sugar.graphics.palette import Palette
     28from sugar.graphics import style
     29
     30_SYSTEM_MOOD_HAPPY = '-happy'
     31_SYSTEM_MOOD_NORMAL = '-normal'
     32_SYSTEM_MOOD_SAD = '-sad'
     33_SYSTEM_MOOD_ERROR = '-error'
     34
     35_ICON_NAME = 'computer'
     36
     37class DeviceView(TrayIcon):
     38
     39    FRAME_POSITION_RELATIVE = 500
     40
     41    def __init__(self):
     42        client = gconf.client_get_default()
     43        self._model = ResourceModel()
     44        self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
     45        TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
     46        self._model.connect('system-mood-changed',
     47                self.__system_mood_changed_cb)
     48        self.create_palette()
     49
     50    def create_palette(self):
     51        self.palette = ResourcePalette(_('System resources'),
     52                self._model)
     53        self.palette.set_group_id('frame')
     54        return self.palette
     55
     56    def __system_mood_changed_cb(self, gobject, mood):
     57        self.icon.props.icon_name = _ICON_NAME + mood
     58
     59class ResourcePalette(Palette):
     60
     61    def __init__(self, primary_text, model):
     62        Palette.__init__(self, label=primary_text)
     63
     64        self._vbox = gtk.VBox()
     65        self.set_content(self._vbox)
     66
     67        self._cpu_text = gtk.Label()
     68        self._vbox.pack_start(self._cpu_text, padding=style.DEFAULT_PADDING)
     69        self._cpu_bar = gtk.ProgressBar()
     70        self._cpu_bar.set_size_request(
     71            style.zoom(style.GRID_CELL_SIZE * 4), -1)
     72        self._vbox.pack_start(self._cpu_bar, padding=style.DEFAULT_PADDING)
     73
     74        self._memory_text = gtk.Label()
     75        self._vbox.pack_start(self._memory_text, padding=style.DEFAULT_PADDING)
     76        self._memory_bar = gtk.ProgressBar()
     77        self._memory_bar.set_size_request(
     78            style.zoom(style.GRID_CELL_SIZE * 4), -1)
     79        self._vbox.pack_start(self._memory_bar, padding=style.DEFAULT_PADDING)
     80
     81        model.connect('resource-status-updated',
     82            self.__resource_status_updated_cb)
     83        model.connect('resource-compute-error',
     84            self.__resource_compute_error_cb)
     85
     86        self._vbox.show()
     87        self._cpu_text.show()
     88        self._cpu_bar.show()
     89        self._memory_text.show()
     90        self._memory_bar.show()
     91
     92    def __resource_status_updated_cb(self, gobject, cpu_in_use,
     93            memory_in_use):
     94        self._cpu_text.set_label(_('CPU in use: %d%%') % cpu_in_use)
     95        self._cpu_bar.set_fraction(cpu_in_use / 100.0)
     96        self._memory_text.set_label(_('Memory in use: %d%%') %
     97            memory_in_use)
     98        self._memory_bar.set_fraction(memory_in_use / 100.0)
     99
     100    def __resource_compute_error_cb(self, gobject):
     101        """
     102        Display and error message since we've hit an exception.
     103
     104        """
     105        # Use the existing _cpu_text label to display the error. Remove
     106        # everything else.
     107        self._cpu_text.set_size_request(
     108            style.zoom(style.GRID_CELL_SIZE * 4), -1)
     109        self._cpu_text.set_line_wrap(True)
     110        self._cpu_text.set_text(_('Cannot compute CPU and memory usage '
     111            'statistics!'))
     112        self._vbox.remove(self._cpu_bar)
     113        self._vbox.remove(self._memory_text)
     114        self._vbox.remove(self._memory_bar)
     115
     116class ResourceModel(gobject.GObject):
     117    __gsignals__ = {
     118        'system-mood-changed': (gobject.SIGNAL_RUN_FIRST,
     119                           gobject.TYPE_NONE,
     120                           ([str])),
     121        'resource-status-updated': (gobject.SIGNAL_RUN_FIRST,
     122                           gobject.TYPE_NONE,
     123                           ([int, int])),
     124        'resource-compute-error': (gobject.SIGNAL_RUN_FIRST,
     125                           gobject.TYPE_NONE,
     126                           ([]))
     127    }
     128
     129    def __init__(self):
     130        gobject.GObject.__init__(self)
     131        self._system_mood = ''
     132        try:
     133            self._cpu_times = self._get_cpu_times_list()
     134        except IOError:
     135            logging.exception('An error ocurred while attempting to '
     136                'read /proc/stat')
     137            self.emit('resource-compute-error')
     138        self._timer = gobject.timeout_add(5000, self.__timer_cb)
     139
     140    def __timer_cb(self):
     141        try:
     142            cpu_in_use = 100 - self._percentage_cpu_available()
     143            memory_in_use = 100 - self._percentage_memory_available()
     144        except IOError:
     145            logging.exception('An error ocurred while trying to '
     146                'retrieve resource usage statistics')
     147            system_mood = _SYSTEM_MOOD_ERROR
     148            self.emit('system-mood-changed', system_mood)
     149            self.emit('resource-compute-error')
     150            return False
     151        else:
     152            self.emit('resource-status-updated', cpu_in_use,
     153                    memory_in_use)
     154
     155            total_load = (cpu_in_use + memory_in_use)
     156            if total_load >= 0 and total_load <= 67:
     157                system_mood = _SYSTEM_MOOD_HAPPY
     158            elif total_load > 67 and total_load <= 133:
     159                system_mood = _SYSTEM_MOOD_NORMAL
     160            elif total_load > 133 and total_load <=200:
     161                system_mood = _SYSTEM_MOOD_SAD
     162            else:
     163                # This should never happen
     164                system_mood = _SYSTEM_MOOD_ERROR
     165                logging.error('Computed system load out of range!')
     166                self.emit('system-mood-changed', system_mood)
     167                self.emit('resource-compute-error')
     168                return False
     169
     170            # Update the frame icon only when system mood changes
     171            if self._system_mood != system_mood:
     172                self._system_mood = system_mood
     173                self.emit('system-mood-changed', system_mood)
     174
     175            return True
     176
     177    def _get_cpu_times_list(self):
     178        """Return various cpu times as read from /proc/stat
     179
     180        This method returns the following cpu times measured
     181        in jiffies (1/100 of a second for x86 systems)
     182        as an ordered list of numbers - [user, nice,
     183        system, idle, iowait] where,
     184
     185        user: normal processes executing in user mode
     186        nice: niced processes executing in user mode
     187        system: processes executing in kernel mode
     188        idle: twiddling thumbs
     189        iowait: waiting for I/O to complete
     190
     191        Note: For systems having 2 or more CPU's, the above
     192        numbers would be the cumulative sum of these times
     193        for all CPU's present in the system.
     194
     195        """
     196        user, nice, system, idle, iowait = \
     197                file('/proc/stat').readline().split()[1:6]
     198        return [int(user), int(nice), int(system), int(idle),
     199                int(iowait)]
     200
     201    def _percentage_cpu_available(self):
     202        """
     203        Return free CPU resources as a percentage
     204
     205        """
     206        cpu_times_new = self._get_cpu_times_list()
     207        cpu_times_current = []
     208        for i in range(len(cpu_times_new)):
     209            cpu_times_current.append(cpu_times_new[i] - \
     210                    self._cpu_times[i])
     211
     212        user, nice, system, idle, iowait = cpu_times_current
     213        cpu_free = (idle + iowait) * 100.0 / sum(cpu_times_current)
     214        self._cpu_times = self._get_cpu_times_list()
     215        return cpu_free
     216
     217    def _percentage_memory_available(self):
     218        """
     219        Return free memory as a percentage
     220
     221        """
     222        for line in file('/proc/meminfo'):
     223            name, value, unit = line.split()[:3]
     224            if 'MemTotal:' == name:
     225                total = int(value)
     226            elif 'MemFree:' == name:
     227                free = int(value)
     228            elif 'Buffers:' == name:
     229                buffers = int(value)
     230            elif 'Cached:' == name:
     231                cached = int(value)
     232            elif 'Active:' == name:
     233                break
     234        return (free + buffers + cached) * 100.0 / total
     235
     236def setup(tray):
     237    if not (os.path.exists('/proc/stat') and os.path.exists('/proc/meminfo')):
     238        logging.warning('Either /proc/stat or /proc/meminfo not present. Not '
     239            'adding the CPU and memory usage icon to the frame')
     240        return
     241    tray.add_device(DeviceView())