OpenScheme : Utilisation CPU et charge de batterie
Par :
Guilhem de Wailly (gdw@erian-concept.com)

et :
Fernand Boéri (boeri@unice.fr)

Résumé
Nous poursuivons la programmation d'exemples utilisant la bibliothèque graphique d'OpenScheme avec un petit programme permettant d'afficher des informations relatives à la charge du processeur et à la charge de la batterie des ordinateurs portables.
L'environnement OpenScheme est disponible sur le CDROM de Linux Magazine. Il est aussi disponible sur www.open-scheme.com. Cet environnement existe en version libre ou commerciale.
Introduction
Ce mois-ci, nous continuons à utiliser la bibliothèque graphique d'OpenScheme pour écrire un petit programme permettant d'afficher l'utilisation des ressources du système. Pour rester simples, les ressources affichées sont l'utilisation du CPU et la charge de la batterie des portables. Le programme est configurable en modifiant les sources. Nous aurions pu utilisation le module OP de OpenScheme permettant de manipuler les fichiers d'initialisation .ini, mais le texte aurait été trop long pour cet article.
Le moniteur crée une fenêtre sur l'écran dans laquelle s'affichent deux règles indiquant la charge du processeur et la charge de la batterie, comme le montre la figure suivante :

Figure 1: Fenêtre du moniteur : la règle du haut indique la charge du CPU, la règle du bas indique la charge de la batterie.
La fenêtre du moniteur est découpée en deux parties : la première affiche la jauge de l'utilisation du processeur. La seconde jauge affiche l'état de la charge de la batterie avec une couleur différente si la batterie est en charge. La fenêtre n'est pas gérée par le gestionnaire de fenêtres ; aussi, elle est affichée sans cadre et sans titre, et elle se déplace dans les bureaux virtuels.
Le programme est organisé autour d'un temporisateur (timer) qui consulte régulièrement l'état du système et affiche les deux jauges. 
Informations Linux
Les informations que l'on souhaite afficher sont situées dans le répertoire /proc des systèmes Linux. Ce programme ne fonctionne donc que sous Linux.
Le répertoire /proc contient des fichiers virtuels dont le contenu correspond à des informations du noyau Linux. Ces fichiers peuvent être consultés par simple lecture.
Les deux fichiers de /proc qui l'on souhaite consulter sont /proc/loadavg pour les informations sur le processeur, et le fichier /proc/apm pour les informations relatives à la batterie. 
Pour consulter le fichier /proc/loadavg, on peut taper :
$ cat /proc/loadavg
0.13 0.00 0.00 2/45 704
L'information qui nous importe est le premier nombre qui correspond à la charge du processeur. La consultation des pages de manuel donnera la signification des autres champs.
L'autre fichier, /proc/apm, peut etre consulatable avec :
$ cat /proc/apm
1.9 1.2 0x03 0x01 0x03 0x09 89% -1 ?
Les informations qui nous intéressent sont la quatrième valeur et la septième valeur. Lorsque la quatrième valeur vaut 0x01, le portable est en charge ; lorsqu'elle vaut 0x00, le portable fonctionne sur la batterie. La septième valeur indique le taux de charge de la batterie qui vaut 100% lorsque la batterie est entièrement chargée.
Timer
Le moniteur que nous allons programmer utilise les timers pour rafraîchir l'affichage de manière périodique. Aussi, nous décrivons dans cette section la bibliothèque correspondante.
La bibliothèque des timer est inclue dans OpenScheme dans le plugin OT. Elle composée des fonctions suivantes :
·	(timer:new proc . milisec)
Cette fonction retourne un nouveau timer attaché à la fonction proc sans argument. Cette fonction sera invoquée toutes les milisec millisecondes, ou avec une valeur par défaut. L'usage de la valeur par défaut est recommandé.
Le timer, une fois créé, n'est pas actif.
L'objet est automatique libéré lorsqu'il n'est plus utilisé.
·	(timer? object)
Cette fonction retourne #t si l'objet est un timer, et #f sinon.
·	(timer:start timer)
Cette fonction démarre le timer donnée en argument.
·	(timer:stop timer)
Cette fonction stoppe le timer donné en argument.
·	(timer:interval timer)
Cette fonction retourne l'intervalle de temps en millisecondes séparant les différentes invocations du timer.
·	(timer:proc timer)
Enfin, cette fonction retourne la procédure attachée au timer. Il est ainsi possible d'enchaîner les différentes procédures.
Les timers sont préhemptifs, c'est à dire qu'ils peuvent survenir à n'importe quel moment. La bibliothèque d'OpenScheme est rendue réentrante, ce qui permet de gérer au plus bas niveau par la machine virtuelle l'ordonnancement des appels.
Le programme
Le programme présenté dans ces lignes est disponible à l'adresse http://www.open-scheme.com/pub/cpuinf.osm.
Fonction principale
La fonction principale est chargée de déclarer un certain nombre de constantes, créer la fenêtre du moniteur et d'initialiser le timer. Nous avons :
; initialisation des bibliothèques
(use 'r4rs) ; bibliothèque standard
(use 'ok)   ; fonctions graphiques
(use 'ot)   ; timer
; constantes
; position (x,y) de la fenêtre
(define X                0)
(define Y                0)

; largeur de la fenêtre
(define WIDTH            50)

; hauteur de chacune des jauges
(define HEIGHT           10)

; marges entre les bords de la fenêtre 
; les jauges
(define MARGIN           2)

; couleur du fond de la fenêtre
(define BACKGROUND/COLOR ok:white)

; couleur de la jauge processeur
(define LOAD/COLOR       ok:red)

; couleur de la jauge batterie
(define APM/COLOR        ok:blue)

; couleur de la jauge de la batterie
; lorsque la batterie n'est pas en 
; charge
(define APM/BAT/COLOR    ok:red)
; fonction principale
(define (main)
 (let* (; création de la fenêtre
        [win   (ok:create-window
                #t
                BACKGROUND/COLOR
                X
                Y
                WIDTH
                (+ HEIGHT HEIGHT 
                   MARGIN MARGIN MARGIN))]
        ; création du timer
        [timer (timer:new (lambda ()
                            (refresh win))
                          1000)])
   ; fonction de rappel pour les touches 
   ; relâchées au dessus de la fenêtre
    (ok:release-callback! win
                         (lambda (key)
                           (timer:stop timer)
                           #f))
   ; fonction de rappel pour le
   ; rafraîchissement
   (ok:refresh-callback! win 
                         (lambda ()
                           (refresh win)
                           #t))
   ; la fenêtre est affichée normalement
   (ok:show win ok:show:normal)
   ; le timer est démarré
   (timer:start timer)
   ; les événements graphiques sont
    ; traités
   (ok:exec)))
La fonction crée la fenêtre de l'application. Le premier argument est la fenêtre parente, ici #t. Lorsque le parent est #t, la fenêtre est créée sans être gérée par le gestionnaire de fenêtres ; elle sera donc affichée sans décorations et échappe à la gestion standard. Si le parent est #f, la fenêtre est gérée par le gestionnaire de fenêtres. Les arguments suivants sont la couleur du fond de la fenêtre, la position (x,y) du coin en haut à gauche de la fenêtre, la largeur et la hauteur de la fenêtre.
Le timer est créé avec une fonction de rappel invoquée toutes les secondes. Cette fonction rafraîchit le contenu de la fenêtre avec la fonction refresh définie plus loin. Le timer n'est pas encore actif ; il le sera après l'appel à la fonction timer:start.
Lorsqu'une touche est relâchée au-dessus de la fenêtre du moniteur, la fonction de rappel correspondante à l'appel à ok:release-callback! est invoquée. La fonction de rappel stoppe le timer est retourne #f, ce qui a pour effet de stopper aussi le traitement des événements initié par l'appel à ok:exec ; la fonction main se termine donc entraînant l'arrêt du programme.
L'affichage du contenu de la fenêtre est confié à la fonction de rappel positionnée par l'appel à ok:refresh-callback!. Là, nous appelons la fonction , définie plus bas.
Rafraîchissement
Le rafraîchissement de la fenêtre se contente d'appeler deux fonctions d'affichage définies plus loin, l'une pour les informations du processeur, l'autre pour les informations de la batterie :
(define (refresh win)
 (let* (; dimensions de la fenêtre
        [rec (ok:rectangle win)]
        ; largeur de la fenêtre
        [w   (vector-ref rec 2)])
   ; affichage des jauges
   (draw-load win w 0)
   (draw-apm  win w (+ MARGIN HEIGHT))
   ; force le traitement des événements
   (ok:show win ok:show:normal)))
Les fonctions d'affichage sont invoquées avec la fenêtre comme argument, ainsi que la largeur de la fenêtre et la position y où l'affichage doit avoir lieu. La fonction ok:show est invoquée pour forcer le traitement des événements par le gestionnaire d'affichage. En effet, la plupart des affichages dans la fenêtre vont être effectués suite à l'activation du timer ; hors sans cet appel, le gestionnaire risque de placer les événements générés dans un tampon et de ne traiter que les derniers événements, rendant du même coup l'affichage incohérent.
Informations du processeur
Cette fonction est chargée d'effectuer l'affichage des informations du processeur. Elle est définie par :
(define (draw-load win w y)
 (let (; lecture de la valeur
       [load (with-input-from-file
                 "/proc/loadavg"
               read)])
   ; interdire le 0 pour la division
   (if (zero? load)
       (set! load 0.001))
   ; dessine la jauge
   (draw-slider win w y
                (inexact->exact
                 (/ (* (- w MARGIN MARGIN)
                       load)
                    (ceiling load)))
                LOAD/COLOR)
   ; dessin des unités
   (let ([l (quotient (- w MARGIN MARGIN)
                      (ceiling load))])
     (do ([i 1           (+ i 1)]
          [x (+ MARGIN l)(+ x l)])
         ((> i (floor load)))
       (ok:draw-line win
                     x MARGIN
                     0 HEIGHT)))))
La première partie de la fonction lit la valeur de cette jauge directement dans le fichier loadavg dans /proc. Cette lecture est effectuée avec la fonction with-input-from-file qui prend comme argument un nom de fichier et une procédure sans argument. Le corps de cette procédure est invoqué avec comme fichier standard d'entrée le contenu du fichier donné. Ici, la procédure est la fonction read qui lit un objet Scheme. Comme le premier champ du fichier est celui qui nous intéresse, un seul appel à read est nécessaire. Si la valeur retournée dans load est nulle, nous la plaçons à une valeur presque nulle de manière à éviter les divisions par zéro.
Ensuite la fonction invoque draw-slider définie plus bas, avec comme arguments la fenêtre, sa largeur et la position ou la jauge dit être dessinée, la largeur de la valeur à indiquer dans la jauge, et enfin sa couleur.
Ensuite, nous traçons des lignes verticales pour indiquer les unités.
Remarquons que la fonction ceilling renvoie la plus petite valeur entière plus grande que la valeur passée en argument, et que la fonction floor renvoie la plus grande valeur entière plus petite que l'argument.
Informations de la batterie
L'affichage des informations relatives à la batterie s'effectue de la même manière que dans la fonction précédente : 
(define (draw-apm win w y)
 (let (; valeurs à lire
       [bat    #f]
       [charge #f])
   ; lecture dans le fichier
   (with-input-from-file
       "/proc/apm"
     (lambda ()
       (read)
       (read)
       (read)
       ; conversion en booléen
       (set! bat (eq? (read) '0x00))
       (read)
       (read)
       ; conversion en entier
       (set! charge
            (string->number
             (substring (symbol->string (read))
                         0 
                         2)))))
   ; dessin de la jauge
   (draw-slider win w y
                (if (negative? charge)
                    0
                    (quotient (* (- w MARGIN MARGIN)
                                 charge)
                              100))
                (if bat APM/BAT/COLOR APM/COLOR))))
La seule difficulté est que la lecture des informations relatives à la batterie à partir du fichier /proc/apm ne peut être effectuée avec un seul read. De plus les valeurs qui nous intéresse dans fichier /proc/apm ne sont pas de objets Scheme ; aussi, il nous faut les convertir.
En ce qui concerne l'affichage, nous utilisons une couleur différente si la batterie est ou non connectée au secteur.
Affichage de la jauge
La dernière fonction du programme dessine une jauge :
(define (draw-slider win w y width color)
 ; dessin de la jauge
 (ok:foreground! win color)
 (ok:background! win color)
 (ok:draw-rectangle win
                    MARGIN (+ y MARGIN)
                    width
                    HEIGHT)
 ; dessin du cadre autour de la jauge
 (ok:foreground! win ok:black)
 (ok:background! win ok:transparent)
 (ok:draw-rectangle win
                    MARGIN (+ y MARGIN)
                    (- w MARGIN MARGIN)
                    HEIGHT))
Les arguments sont la fenêtre sur laquelle la jauge doit être dessinée, la largeur de la fenêtre, la position y de la jauge, la largeur de la valeur de la jauge et sa couleur. La seule remarque concerne l'utilisation de la couleur transparent qui permet de dessiner le rectangle sans fond.
Exécution
Il ne reste plus qu'à invoquer la fonction main à la fin du fichier :
(main)
Pour exécuter le programme, nous tapons sur la ligne de commande du shell la commande suivante :
$ ./osm --exec cpuinf.osm
Il est aussi possible de placer en haut du fichier la ligne suivante :
#! ./osm --exec
De cette manière, et en rendant le fichier exécutable, il est possible d'invoquer le programme directement.
Améliorations
Le programme actuel permet d'afficher les jauges pour l'utilisation du processeur et de la batterie. Nous pourrions ajouter d'autres jauges en consultant d'autres fichiers de /proc. Dans ce cas, il est nécessaire d'écrire le nom des jauges afin de les différencier.
Le programme utilise des constantes pour initialiser certains paramètres importants. Il serait possible de créer un fichier d'initialisation et d'utiliser les fonctions du module OP pour faciliter la configuration.
Il serait aussi possible d'utiliser la fonction osm:parse-options qui permet de définir facilement des options de la ligne de commande des applications.
Les auteurs

Guilhem de Wailly, directeur de la société Erian Concept : support, formations, configurations, administration, développements Linux. Environnement OpenScheme.
http://www.erian-concept.com

Pr Fernand Boéri, Professeur à l'Université de Nice - Sophia-Antipolis, membre de l'unité SPORTS du laboratoire I3S, spécialiste en modélisation, évaluation de performances et architectures parallèles.
http://www.i3s.unice.fr
Références
Programmation orientée objet
¨	OMT: 1-Modélisation et conception orientées objet
James Bumbaugh et al.
Masson / Prentice Hall
Scheme
¨	Structure et Interprétation des programmes informatiques
H. Abelson, GJ. Sussman
InterEdition
¨	The Scheme Programming Languages - Ansi Scheme 
R. Kent Dybvig
Prentice Hall
¨	Les langages Lisps -  Christian Queinnec
InterEdition
¨	Programmer avec Scheme - J. Chazarin
Thomson Publishing
¨	Revised4 Report on the Algorithmic Language Scheme
W. Clinger, J. Rees
ftp://ftp.nj.nec.com/pub/kelsey
Environnements Scheme
¨	Scm - A. Jaffer
http://www-swiss.ai.mit.edu/~jaffer
La référence des interprètes Scheme. Très petit, rapide, pour beaucoup de plates-formes, extensible.
¨	PCScheme - Texas Instrument
ftp://cui.unige.ch/public/pcs/pcscheme.exe
Un très bon environnement de programmation Scheme pour DOS, avec éditeur intégré.
¨	DrScheme - Rice University
http://www.cs.rice.edu/CS/PLT/
Environnement Scheme libre très avancé.
¨	ChezScheme - Cadence, Inc
http://www.scheme.com/
Environnement Scheme très performant.
¨	Inlab Scheme - Inlab Software GmbH
http://www.munich.net/inlab/scheme/
Environnement commercial
¨	EdScheme, 3Dscheme - Scheme, Inc
http://www.schemers.com/
Environnement de programmation Scheme pour Windows.
¨	OpenScheme - Erian Concept
http://www.open-scheme.com
Environnement professionnel de programmation Scheme comprenant un interprète, un compilateur et un débogueur symbolique. 
Existe en version libre et commerciale, pour Linux, FreeBSD, Solaris, Windows et BeOS, sur systèmes Intel.