Graphique interactif et dynamique de la consommation électrique

jeudi 12 juin 2014
par  Finizi
popularité : 14%

Voici quelques explications pour obtenir des courbes interactives dynamiques des historiques de consommation.

=> Historiques, car on commence par afficher la courbe des consommations électriques des 2 derniers jours, minute par minute.

=> Dynamique car la courbe à l’écran va se mettre à jour au fil du temps

=> Interactive car en déplaçant la souris sur la courbe, il est possible d’afficher les valeurs correspondantes. Il est aussi possible de zoomer sur une partie de la courbe.

De plus, dans cet exemple, 3 jeux de données différents sont disponibles sans qu’il soit nécessaire de recharger la page.
Ces explications sont basées sur la page mise en oeuvre ici.


 Le principe

Le principe est assez simple.
Il y a 4 documents à générer :

  • Une page HTML pour présenter les données
  • Un script JavaScript pour mettre tout cela en forme et rendre l’ensemble dynamique
  • Un document csv pour récupérer l’historique sur les jours passés
  • Un document json pour obtenir le flux dynamique de mise à jour.

    Pour que ce soit aussi simple, 3 autres fichiers JavaScript sont utilisés. Ce sont les bibliothèques de fonctions suivantes qui sont des bibliothèques JavaScript libres :
  • jQuery : Porte sur l’interaction entre JavaScript (y compris Ajax) et HTML, et a pour but de simplifier des commandes communes de JavaScript.
  • dygraphs : Bibliothèque open source de fonctions graphiques.
  • AngularJS : A pour but de faciliter la réalisation d’applications web monopages en gérant les communications entre la page web et le serveur.

    En ce qui concerne les bibliothèques, 2 options possibles :
  • Utiliser les versions disponibles sur Internet. Cela permet d’utiliser une version qui reste à jour, et, comme ces bibliothèques sont très utilisées, de bénéficier des caches mis en place par les infrastructures de réseau pour obtenir le fichier le plus rapidement possible.
  • Les copier sur le serveur, ce qui est très avantageux dans le cas d’un Intranet et permet aux pages de fonctionner rapidement même avec une connexion Internet de piètre qualité, voir même sans connexion.

    Le présent article ne traitera pas de la façon de générer les documents csv et json. Cela fera sans doute l’objet d’autres articles.

 Le HTML

Le fichier html est assez basique.

  • Remarquons l’attribut ng-app="DetApp" à la balise html pour déclarer l’application AngularJS.
  • Ensuite les liens vers les fichiers JavaScripts, les 3 bibliothèques et le script particulier décrit ci-après.
  • Un petit peu de CSS pour mettre en forme les textes cliquables(ce ne sont pas de vrais liens) qui permettent de changer le type de données affichées.
  • Le texte reprenant les détails fournis par le compteur EDF
  • Les textes cliquables pour changer de courbe affichée.
  • Le graphique

 Le JavasScript

Le code JavaScript contenu dans ce fichier permet de gérer principalement les données. Bien sur, il y a quelques instructions relatives à la présentation en fonction du jeu de données demandé.

  • dateFormated()
    La première fonction dateFormated() permet juste de formater la date affichée du point sélectionné lors du survol de la courbe par la souris.
  • DetApp
    C’est le nom du module AngularJS utilisé.
  • DetApp.config()
    C’est une astuce qui permet d’utiliser AngularJS avec le langage de template de Django et bottle. En effet, AngularJS (coté client) et bottle et Django (coté serveur) utilisent la syntaxe des doubles accolades pour évaluer des expressions. Cette astuce permet de spécifier à AngulaJS d’utiliser les doubles parenthèses séparées par un tiret bas (_( & )_) à la place des doubles accolades {{ & }} qui pourront de ce fait être utilisées coté server. Noter quz dans le cas présent cela est inutile vu que le fichier html est statique.
  • dataSerie
    C’est la variable qui permet de faire le distingo entre les données brutes (toutes les minutes) et les données journalières qui sont présentées différemment.
  • kermeloEdfCtrl()
    C’est la fonction principale utilisée par AngularJS. Elle est appelée à l’initialisation de la page. Elle réalise les actions suivantes :
    • Préciser que l’on affiche les données historique minute par minute des 2 derniers jours.
    • Initialiser l’actualisation des données en précisant l’url à appeler. Les données sont reçues sous cette forme : {"idxC": "218026", "idxB": "93543", "idxA": "245016", "ts": "17:20", "idxD": "94155", "pa": "12433"} où :
      • idx? donne la valeur du compteur correspondant
      • pa est la puissance apparente instantannée
      • ts l’heure fournie par le compteur
    • Calculer le cumul (il s’agit ici d’un compteur EDF avec 4 tarifs différents).
    • Ajouter le nouveau point à la liste des points déjà affichés, en mettant la date correctement en forme. En effet, JSON renvoie plus d’informations que ce qui est nécessaire à la courbe. Mais il y a la partie texteuelle à mettre à jour au dessus du graphique.
    • Définir l’intervalle de temps (timeout) entre chaque interrogation du serveur pour obtenir une mise à jour des données
  • chooseDaily()
    Cette fonction permet de choisir les données journalières grâce aux actions suivantes :
    • Préciser l’url à appeler
    • Change la mise en forme du graphique (titre, libellés, présentation des points, ...)
  • chooseFlow()
    Cette fonction permet de choisir des données minutes par minutes, depuis un nombre de jours passé en paramètre. Les actions suivantes sont réalisées :
    • Préciser l’url à appeler
    • Change la mise en forme du graphique (titre, libellés, présentation des points, ...)

 Ce que l’on obtient

PNG - 49.8 ko

 Le code du fichier HTML

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  3. <html ng-app="DetApp">
  4.   <head>
  5.     <meta charset="UTF-8">
  6.     <script src="/static/jquery-1.8.3.min.js" type="text/javascript"></script>
  7.     <script src="/static/dygraph.js" type="text/javascript"></script>
  8.     <script src="/static/angular.min.js" type="text/javascript"></script>
  9.     <script src="/static/kermeloEdfCtrl.js" type="text/javascript"></script>
  10.     <style>
  11.       #divConso, #divCmd {
  12.         display:inline-block;  
  13.         height: 80px;
  14.         vertical-align: top;
  15.       }
  16.     </style>
  17.   </head>
  18.   <body>
  19.     <h3>&nbsp; &nbsp; Compteur EDF:</h3>
  20.     <div id="divConso" style="width:950px; ">
  21.       <ul ng-controller="kermeloEdfCtrl" ng-init="getJson()">
  22.         <li>Puissance apparente = <b>(_(data.pa)_)</b>&nbsp; VoltAmp&egrave;res</li>
  23.         <li>Index g&eacute;n&eacute;ral = <b>(_(cumul)_)</b>&nbsp; kWh &nbsp; &nbsp; <small>( d&eacute;tail: &nbsp; Index A = <b>(_(data.idxA)_)</b>&nbsp; - &nbsp;Index B = <b>(_(data.idxB)_)</b>&nbsp; - &nbsp;Index C = <b>(_(data.idxC)_)</b>&nbsp; - &nbsp;Index D = <b>(_(data.idxD)_)</b> )</small></li>
  24.       </ul>
  25.     </div>
  26.     <div id="divCmd" style="width:220px; background-color: #FAEBFA; padding-left: 20px">
  27.       <a href="#" onclick="javascript: chooseFlow(2);">Conso en continue (2jours)</a><br/>
  28.       <a href="#" onclick="javascript: chooseFlow(14);">Conso en continue (14jours)</a><br/>
  29.       <a href="#" onclick="javascript: chooseDaily();">Conso journali&egrave;re</a>
  30.     </div>
  31.     <br/>
  32.     <br/>
  33.     <div id="graph1" style="width:1200px; height:500px"></div>
  34.     <script>
  35.       var gGraphData = "date,pa";
  36.       var gGraph = new Dygraph(
  37.         document.getElementById("graph1"),
  38.         gGraphData,
  39.         {
  40.           legend: 'always',
  41.           labelsDivStyles: { 'textAlign': 'right' },
  42.           colors: [ "#C031C1" ],
  43.           rangeSelectorPlotStrokeColor: "#e3a9e4",
  44.           rangeSelectorPlotFillColor: '#f6e1f6'
  45.         }
  46.       );
  47.     </script>
  48.   </body>
  49. </html>

 Le code du fichier JavaScript

  1. function dateFormated() {
  2.   var d = new Date(), h = d.getHours(), j = d.getDate(), m = d.getMonth() + 1, i = d.getMinutes(), s = d.getSeconds();
  3.   return d.getFullYear() + '-' + (m < 10 ? '0' + m : m) + '-' + (j < 10 ? '0' + j : j) + ' ' + (h < 10 ? '0' + h : h) + ':' + (i < 10 ? '0' + i : i) + ':' + (s < 10 ? '0' + s : s);
  4. }
  5. var DetApp = angular.module('DetApp', []);
  6. DetApp.config(function($interpolateProvider) {
  7.   $interpolateProvider.startSymbol('(_(');
  8.   $interpolateProvider.endSymbol(')_)');
  9. });
  10. var dataSerie = 'flow';
  11. function kermeloEdfCtrl($scope, $http, $timeout) {
  12.   if (typeof gGraph != 'undefined') {
  13.     chooseFlow(2);
  14.   }
  15.   $scope.getJson = function() {
  16.     $http({method: 'GET', url: '/kermelo/json'}).
  17.     success(function(data, status) {
  18.       $scope.cumul = eval(data.idxA) + eval(data.idxB) + eval(data.idxC) + eval(data.idxD);
  19.       $scope.data = data;
  20.       if (dataSerie == 'flow') {
  21.         $scope.status = status;
  22.         if (typeof gGraph != 'undefined') {
  23.           gGraphData += '\n' + dateFormated() + ',' + data.pa;
  24.           gGraph.updateOptions({file: gGraphData});
  25.         }
  26.       }
  27.     }).
  28.     error(function(data, status) {
  29.       $scope.status = '??';
  30.     });
  31.     $timeout($scope.getJson, 60200);
  32.   };
  33. }
  34. function chooseDaily() {
  35.   $.ajax({
  36.     type: "GET",
  37.     url: "/seriePa.csv/daily",
  38.     dataType: "text",
  39.     success: function(data) {
  40.       dataSerie = 'daily';
  41.       gGraph.updateOptions({
  42.         file: data,
  43.         title: 'Consommation journali&egrave;re',
  44.         ylabel: 'Consommation (kWh)',
  45.         showRangeSelector: false,
  46.         drawPoints: true,
  47.         pointSize: 5,
  48.         highlightCircleSize: 8
  49.       });
  50.     },
  51.     error: function() {
  52.       alert("error");
  53.     }
  54.     })
  55. }
  56. function chooseFlow(nJours) {
  57.   $.ajax({
  58.     type: "GET",
  59.     url: "/seriePa.csv/flow",
  60.     data: 'nDays=' + nJours,
  61.     dataType: "text",
  62.     success: function(data) {
  63.       gGraphData = data;
  64.       dataSerie = 'flow';
  65.       gGraph.updateOptions({
  66.         'file': gGraphData,
  67.         'title': 'Historique des consommations (' + nJours + ' derniers jours)',
  68.         'ylabel': 'Puissance (VA)',
  69.         'showRangeSelector': true,
  70.         'drawPoints': false,
  71.         'pointSize': 1,
  72.         'highlightCircleSize': 3
  73.       });
  74.     },
  75.     error: function() {
  76.       alert("error");
  77.     }
  78.   })
  79. }

 Un exemple JSON

  1. {"idxC": "218026", "idxB": "93543", "idxA": "245016", "ts": "17:20", "idxD": "94155", "pa": "12433"}

 Deux extraits de CSV

Données minute par minute

  1. date,pa
  2. 2014-06-09 21:36:50,6260
  3. 2014-06-09 21:37:50,6278
  4. 2014-06-09 21:39:06,6289
  5. 2014-06-09 21:40:06,6251
  6. 2014-06-09 21:41:07,6142
  7. ...

Données journalières

  1. date,kWh
  2. 2014-04-02,197
  3. 2014-04-03,244
  4. 2014-04-04,250
  5. 2014-04-05,111
  6. 2014-04-06,97
  7. ...

Navigation

Articles de la rubrique