Archives par étiquette : php

4 – Module 1 – Date et Heure

Rentrons dans le vif du sujet avec ce premier module qui affiche la date et l’heure.

jarvis_screenshot_heureCe module est très simple, entièrement réalisé en JavaScript. Le module est baptisé : horloge

index.php

Dans la page d’index, c’est donc la balise <div> avec l’id horloge qui nous intéresse.

 <div id="horloge"></div>

La balise reste vide, et sera « remplie » par la fonction JavaScript.

style.css

Comme pour tous les modules, il convient de créer un div dont l’id correspond au nom du module (ici horloge) qui contiendra les informations à afficher, puis de définir sa taille et sa position sur l’écran.

/* horloge */

div#horloge
{
  right             : 0px;
  top               : 0px;
  position          : absolute;
  overflow          : hidden;
  background-color  : rgba(0, 0, 0, 1);
  color             : #FFF;
  font-weight       : bold;
  text-align        : center;
}

div.horloge_heure
{
  font-size         : 180px;
  background-color  : rgba(0, 0, 0, 1);
}

div.horloge_date
{
  font-size         : 50px;
  background-color  : rgba(0, 0, 0, 1);
}

span.horloge_grey
{
  color             : #888;
  font-size         : inherit;
}

javascript.js

Cette fonction très simple se base sur l’heure du système, et affiche les heures sur 24h et les minutes en gros, puis juste en dessous, le jour de la semaine, je jour dans le mois, le nom du mois et l’année.

var horloge_timeout;

function horloge()
{
  dows  = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"];
  mois  = ["janv", "f&eacute;v", "mars", "avril", "mai", "juin", "juillet", "ao&ucirc;t", "sept", "oct", "nov", "d&eacute;c"];

  now          = new Date;
  heure        = now.getHours();
  min          = now.getMinutes();
  sec          = now.getSeconds();
  jour_semaine = dows[now.getDay()];
  jour         = now.getDate();
  mois         = mois[now.getMonth()];
  annee        = now.getFullYear();

  if (sec < 10){sec0 = "0";}else{sec0 = "";}
  if (min < 10){min0 = "0";}else{min0 = "";}
  if (heure < 10){heure0 = "0";}else{heure0 = "";}

  horloge_heure   = heure + ":" + min0 + min;
  horloge_date    = "<span class='horloge_grey'>" + jour_semaine + "</span> " + jour + " " + mois + " <span class='horloge_grey'>" + annee + "</span>";
  horloge_content = "<div class='horloge_heure'>" + horloge_heure + "</div><div class='horloge_date'>" + horloge_date + "</div>";

  $("#horloge").html(horloge_content);

  horloge_timeout = setTimeout("horloge()", 1000);
}

Toutes les 1000 ms, soit chaque secondes, le contenu de la div horloge est mis à jour avec le contenu de la variable horloge_content. Vous pouvez d’ailleurs afficher les secondes sur l’écran si vous le souhaitez 🙂

Et voilà pour ce premier module très très simple mais parmis les plus utiles, pour se mettre en jambe 🙂

 

3 – Principe de base

Une page pour les afficher tous

Voici l’arborescence des fichiers nécessaires à l’affichage des différents modules.

  • index.php : page principale qui est appelée par le navigateur
  • ajax.php : contient le code exécuté lors des requêtes AJAX
  • inc.php : contient les fonctions PHP
  • javascript.js : contient les fonctions Javascript
  • style.css : feuille de style

Structure on-ne-peut-plus simple. La page « index.php » est appelée par le navigateur, et une fonction JavaScript se charge d’afficher le contenu de chaque module au bon endroit dans la page et de rafraichir l’affichage à intervalle régulier.

Note : Vous pourrez télécharger tous les fichiers dans une archive dans le dernier article du projet.

index.php

La page d’index se contente de positionner les balises <div> dans lesquelles seront affichés chaque module.

Dans la balise <head>, il y a une balise <meta> dont le but est de rafraichir la page toute les 3600 secondes (soit toutes les heures). Ainsi, toute la page sera rechargée, les compteurs JavaScript réinitialisés, les mise à jour du code sont prises en compte, etc…

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title>Jarvis</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <meta http-equiv="Content-Language" content="Fr"/>
    <meta name="Author" lang="fr" content="Torna"/>
    <meta name="Copyright" content="© Torna"/>
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="refresh" content="3600;url=index.php">
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="javascript.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css"/>
  </head>
  <body>

    <div id="main">
      <!--div id="etalon">Jarvis <?php //echo date("Y-m-d H:i:s"); ?></div-->
      <div id="horloge"></div>
      <div id="meteo"></div>
      <div id="meteo_black"></div>
      <div id="ts3"></div>
      <div id="ping"></div>
      <div id="analytics"></div>
      <div id="vpn"></div>
      <div id="latency"></div>
      <div id="earth"><img id="img_earth" src="pict/blank.png"></div>
      <div id="moon"><img id="img_moon" src="pict/blank.png"></div>
      <div id="ifstat_oberon_up"><img id="img_oberon_up" src="pict/blank.png"></div>
      <div id="ifstat_oberon_down"><img id="img_oberon_down" src="pict/blank.png"></div>
    </div>

    <?php include('inc.php'); echo analytics(); ?>

  </body>
</html>

Pour les modules qui affichent des images (phases terre et lune, graphique de bande passante), il convient d’insérer une image vide « blank.png » afin d’éviter l’affichage d’une erreur dans la page en attendant que la requète AJAX chargée de l’affichage de l’image soit terminée.

ajax.php

C’est ce fichier qui est appelé par les requètes AJAX et qui est chargé d’appeler la bonne fonction à exécuter.

<?php

  header('Content-type: text/html; charset=utf-8');
  require_once('inc.php');

  if(isset($_REQUEST['block'])){$block = $_REQUEST['block'];}else{$block = 'none';}

  /////////////////////////////////////////////////
  //  LATENCY
  /////////////////////////////////////////////////

  if($block == 'latency'){
    echo latency();
  }

  /////////////////////////////////////////////////
  //  TS3
  /////////////////////////////////////////////////

  else if($block == 'ts3'){
    echo ts3();
  }

  /////////////////////////////////////////////////
  //  METEO
  /////////////////////////////////////////////////

  else if($block == 'meteo'){
    echo meteo();
  }

  /////////////////////////////////////////////////
  //  PING
  /////////////////////////////////////////////////

  else if($block == 'ping'){
    echo ping();
  }

  /////////////////////////////////////////////////
  //  VPN PPTPD
  /////////////////////////////////////////////////

  else if($block == 'vpn'){
    echo vpn();
  }

  /////////////////////////////////////////////////
  //  IFSTAT
  /////////////////////////////////////////////////

  else if($block == 'ifstat'){
    imagickHisto ($_REQUEST['max'], $_REQUEST['eth'], $_REQUEST['up_down']);
  }

?>

Un bloc de code pour chaque module. En fonction de la variable $block passée en paramètre de la requète AJAX, on appelle la fonction ciblée.

inc.php

Ce fichier contient les fonctions appelées par le fichier ajax.php. Même principe, un bloc de code pour chaque module.

En gros, une fonction par module 🙂 Le détail de chaque fonction fera l’objet d’un article à suivre.

javascript.js

Ce fichier contient les fonctions JavaScript qui vont lancer les requêtes AJAX à intervalle réuglier.

/* initialisation des fonctions de tous les modules au chargement de la page */
$(document).ready(function() {
   fonction_nom_du_module_1 ();
   fonction_nom_du_module_2 ();
   ...
});

/* initialisation des variables timeout pour chaque module */
var timeout_nom_du_module_1; 
var timeout_nom_du_module_2;
...

/* définition de la fonction pour le module nom_du_module_1 */

function nom_du_module_1 ()
{
  $.ajax({
    async : false,
    type: "GET",
    url: "./ajax.php",
    data: "block=nom_du_module_1",
    success: function(html){
      $("#nom_du_module_1").html(html);
    }
  });

  g = setTimeout("nom_du_module_1()", 10000);
}

Je ne mets ici que la structure gobale du fichier pour expliquer le principe. Le fichier complet sera disponible au téléchargement dans le dernier article.

style.css

Ce fichier contient la feuille de style de la page, c’est elle qui contient toutes les options de mise en forme des éléments de la page (position, taille, couleurs, etc…).

/* init */

body
{
  background-color  : rgba(0, 0, 50, 1);
  font-family       : Verdana, "Courier New", "Lucida Console";
  font-size         : 14px;
  font-weight       : normal;
  padding           : 0px;
  margin            : 0px;
  overflow          : hidden;
}

div, table, tr, td, th, h1, h2, h3, a, span, p, pre, fieldset, input, textarea, form, img, select
{
  font-family       : Verdana, "Courier New", "Lucida Console";
  font-size         : 14px;
  font-weight       : normal;
  padding           : 0px;
  margin            : 0px;
  color             : #FFF;
}

/* main */

div#main
{
  width             : 1366px;
  height            : 768px;
  position          : relative;
  overflow          : hidden;
  background-color  : rgba(0, 0, 0, 1);
  color             : #FFF;
}

La div qui a pour id main est la div qui contient toutes les autres div de chaque module. Ici, je fixe sa taille en pixel à la résolution native de mon écran 1366×768 px.

La couleur de fond (background-color) est fixée au noir mais vous pouvez choisir ce qui vous plaît 🙂

10 – Le serveur de monitoring

Au départ je souhaitais pouvoir prendre la main à distance sur le Pi pour m’assurer que tout fonctionne bien durant la semaine. Cependant, les contraintes d’autonomie du Pi sur batterie me forçant à ne temporiser sont fonctionnement 2 minutes toutes les 10 minutes, la fenêtre de tir pour choper le Pi lorsqu’il est up est franchement serrée.

Sans compter sur l’autonomie, je ne pourrai pas surveiller le Pi à longueur de journée pour voir si tout fonctionne bien, que rien ne gêne les prises de vue, etc,… Il faut donc que le Pi se manifeste tout seul, régulièrement : c’est le principe du « battement de coeur ». Le Pi envoi de lui même des battements de coeur réguliers, et si les battements de coeur s’arrêtent, c’est que quelque chose à mal tourné.

L’idée est donc d’utiliser un serveur distant, qui accusera réception de ces battements de coeur, et m’enverra un mail pour me dire que tout va bien. Chaque battement de coeur sera accompagné d’une miniature de la dernière photo prise. Tout est enregistré dans une base de données, et j’ai prévu une page qui récapitule toutes les battements de coeur enregistrés avec leur photo miniature. Ce qui me permettra également d’avoir un aperçu des travaux en direct.

Inutile d’envoyer un battement de coeur à chaque prise de vue. Un mail toutes les 10 minutes, c’est trop. Deux mails par heure, c’est mieux 🙂

Serveur web – Apache

Très concrètement, le serveur de monitoring est un simple serveur web Apache, accompagné d’une base de données MySQL et de quelques scripts PHP.

Je ne détail pas ici l’installation et la configuration de ces composants. C’est un sujet bien à part qui mériterait un chapitre complet. Si vous voulez faire pareil, vous pouvez utiliser un hébergement mutualisé chez Free ou OVH (les ftp Free mis à disposition des Freenautes font également parfaitement l’affaire).

Base de données

Je n’ai besoin que d’une seule table pour enregistrer chaque battement de coeur ainsi que la photo associée. Voici le schéma de la table heartbeat que j’utilise :

CREATE TABLE `heartbeat` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date` int(11) NOT NULL,
  `log` text COLLATE utf8_bin NOT NULL,
  `img` text COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;

Description des champs :

  • id : un simple indexe auto incrémenté
  • date : champ entier pour stocker la date du battement de coeur sous la forme d’un timestamp
  • log : champt text permettant d’enregistrer des infos diverses
  • img : champ text contenant le nom de la miniature de la photo associée au battement de coeur

Fonctions et fichier de configuration

J’ai rassembler les fonctions génériques que j’utilise dans le fichier func.php que vous pouvez télécharger ici : func.php.tar.gz

L’ensemble des paramètres de configuration des scripts PHP sont rassemblés dans le fichier conf.php que vous pouvez télécharger ici : conf.php.tar.gz

Script PHP heartbeat.php

C’est ce script qui va être appelé par la commande curl comme expliqué dans le chapitre précédent. Sont rôle est de réceptionner la photo miniature, d’enregistrer la date de réception, et d’envoyer un mail.

Je vais à l’essentiel, le code de ce script est donc très minimaliste :

<?php

header('Content-type: text/html; charset=utf-8');

// inclusion des fichiers requis
require_once('conf.php');
require_once('func.php');

// initialisation de la session
$session_id = sessionStart();
// connexion à la base de données
$db_connect = dbConnect();
// nettoie la variable $_REQUEST
$_REQUEST   = clearRequest ($_REQUEST);

// enregistre la tentative de connexion dans un fichier de log
logIt('access', 'heartbeat connection ...');

// vérifie que le token donné correspond à celui attendu ;)
if(!empty($_REQUEST['token']) && $_REQUEST['token'] == $conf['token']['heartbeat']) {

  // si le fichier est bien réceptionné
  if (is_uploaded_file($_FILES['img']['tmp_name'])) {
    if (move_uploaded_file($_FILES['img']['tmp_name'], $conf['upload_dir'].'/'.$_FILES['img']['name'])) {

      $field_data = array (
                              'date'  => time(),
                              'img'   => $_FILES['img']['name'],
                              'log'   => ''
                          );
      // enregistre le battement de coeur en base de données dans la table heartbeat
      $add        = addData ('heartbeat', $field_data);

      // titre du mail à envoyer
      $mail_title = 'Pi TimeLapse - HeartBeat '.date("d.m.y H:i:s");

      // corps du mail à envoyer, contenant la date et la minature
      $mail_core  = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
                      <html>
                        <head>
                          <title>'.$conf['site_name'].'</title>
                          <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
                          <meta http-equiv="Content-Language" content="Fr" />
                          <META name="Author" lang="fr" content="MagdiWeb" />
                          <META name="Copyright" content="© MagdiWeb" />
                        </head>
                        <body>
                          '.$mail_title.'<br>
                          '.$field_data['log'].'<br>
                          <img src="http://pi_timelapse/'.$conf['upload_dir'].'/'.$_FILES['img']['name'].'">

                        </body>
                      </html>';

      // envoie le mail
      $mail       = mailIt ('mon_adresse_mail@mail.fr', $mail_title, $mail_core);
      logIt('access', $mail_title);
    }
    else {
      logIt('access', 'unable to mv uploaded file '.$_FILES['img']['name']);
    }
  }
  else{
    logIt('access', 'error during file upload ...');
  }

}
else{
  logIt('access', 'wrong heartbeat token ...');
}

// écrit l'ensemble des messages/logs dans un fichier
writeLog ();

?>

Voici une screenshot du mail que je reçois sur mon smartphone lors de la réception d’un battement de coeur de la part du Pi (sur la photo on voit un bout de mon bureau,… c’est pour les tests 😉 :

photo

Dashboard

J’ai également prévu une page type dashboard qui affiche toutes les miniatures reçues avec les dates. Pour le moment, je ne publie pas cette page, mais lorsque le chantier commencera, je la synchroniserai avec ce blog afin que vous puissiez suivre en directe l’évolution des travaux, et constater que le dispositif fonctionne bien 🙂

Toujours aussi minimaliste, le but est simplement d’afficher l’ensemble des miniatures sous forme d’une mosaïque d’images.

<?php

header('Content-type: text/html; charset=utf-8');

require_once('conf.php');
require_once('func.php');

$session_id = sessionStart();
$db_connect = dbConnect();
$_REQUEST   = clearRequest ($_REQUEST);

if(!empty($_REQUEST['token']) && $_REQUEST['token'] == $conf['token']['index']) {

$heartbeats = selectAll ('heartbeat');

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title><?php echo $conf['site_name']; ?></title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta http-equiv="Content-Language" content="Fr" />
    <META name="Author" lang="fr" content="MagdiWeb" />
    <META name="Copyright" content="© MagdiWeb" />  
    <link rel="stylesheet" type="text/css" href="style.css" />
  </head>
  <body>
    <?php
      $nb_heartbeat = sizeof($heartbeats);
      echo '('.$nb_heartbeat.' beats)';
    ?>
    <table>
    <?php
      $c = 0;
      foreach($heartbeats as $heartbeat) {
        if($c == 0){echo '<tr>';}
        echo '<td>'.humanDate ($heartbeat['date'], 'full').'<br><img src="'.$conf['upload_dir'].'/'.$heartbeat['img'].'"></td>';
        $c++;
        if($c == 4){$c=0; echo '</tr>';}
      }
    ?>
    </table>

  </body>
</html>

<?php

}else{
  logIt('access', 'wrong index token ...');
}

writeLog ();

?>

Voici une screenshot de ce que cela donne pour l’instant (toujours avec des photos de test 😉 :

Screenshot-1