Site icon Meccanismo Complesso

jqPlot: come rappresentare dati in tempo reale

Benvenuto, in questo articolo vedremo come realizzare un grafico che rappresenti i dati in tempo reale (real-time) utilizzando libreria jqPlot.

Questa tipologia di chart è molto utile per chi debba effettuare una lettura diretta. I grafici in tempo reale sono indispensabili ogni volta che sia necessario monitorare l’andamento di una grandezza fisica nel tempo, o di una particolare quantità o anche una variabile in generale. Per esempio, un esempio classico di grafici in tempo reale è quello che visualizziamo ogni volta che apriamo Task Manager su Windows. Infatti una volta aperto questo tool possiamo vedere il consumo di risorse della CPU, della memoria fisica e della rete attraverso tutta una serie di grafici che si aggiornano in continuazione (vedi Fig.1). 

Fig.1: Windows Task Manager mostra alcuni esempi di grafici in tempo reale

Questa tipologia di grafici sono caratterizzati dal fatto che vengono aggiornati ad intervalli di tempo regolari, che possono andare da pochi secondi a persino ore o giorni. Ciò dipende dall’indice di variabilità della grandezza osservata. Ci sono tantissimi altri casi simili a cui possiamo pensare, per esempio, quando stiamo monitorando la temperatura esterna durante tutte le ore del giorno, oppure quando si segue l’andamento di un indice di borsa in particolare. Proprio a causa della loro utilità, è importante saper realizzare questo genere di grafici su Web. Grazie alla libreria jqPlot, vedremo come questa operazione sia facile.

Nel libro che ho recentemente scritto, Beginning JavaScript Charts with jqPlot, Highcharts and D3, ed.Apress (2013), oltre a tantissimi altri esempi, questo argomento dei real-time data viene trattato in maniera approfondita, sia utilizzando la libreria jqPlot che la libreria D3. Consiglio vivamente a chi voglia approfondire ulteriormente l’argomento a leggere i due capitoli (Capitolo 17 e Capitolo 26) del libro che trattano in maniera specifica questo argomento

Ma adesso passiamo all’esempio vero e proprio. Partiamo mostrando il codice intero della pagina HTML su cui svilupperemo l’esempio.

<HTML> 
<HEAD> 
<!--[if lt IE 9]>
<script type="text/javascript" src="http://cdn.jsdelivr.net/excanvas/r3/excanvas.js"></script>
<![endif]--> 
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> 
<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.js">
</script> <script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.dateAxisRenderer.min.js"></script> 
<link href="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.css" rel="stylesheet" type="text/css" /><script type="text/javascript">

$(document).ready(function(){    
    var t = 2000;    
    var x = (new Date()).getTime(); 
    // current time    
    var n = 20;    
    data = [];    
    for(i=0; i<n; i++){         
        data.push([x - (n-1-i)*t,0]);      
    }       
    var options = {             
        axes: {                  
            xaxis: {                        
                numberTicks: 4,
                renderer:$.jqplot.DateAxisRenderer,
                tickOptions:{formatString:'%H:%M:%S'}, 
                min : data[0][0],  
                max: data[data.length-1][0]
            },
            yaxis: {             
                min:0,              
                max: 1,             
                numberTicks: 6,
                tickOptions:{formatString:'%.1f'}                 
            }             
       },             
       seriesDefaults: {                  
           rendererOptions: { smooth: true}  }      
       };      
       var plot1 = $.jqplot ('myChart', [data],options); 
       $('button').click( function(){               
           doUpdate();             
           $(this).hide();      
       });    
       function doUpdate() {             
            if(data.length > n-1){          
                 data.shift();       
            }       
            var y = Math.random();       
            var x = (new Date()).getTime();       
            data.push([x,y]);       
            if (plot1) {      
                 plot1.destroy();       
            }       
            plot1.series[0].data = data;        
            options.axes.xaxis.min = data[0][0];
            options.axes.xaxis.max = data[data.length-1][0];
            plot1 = $.jqplot ('myChart', [data],options);
            setTimeout(doUpdate, t);    
       } 
}); 
</script> 
<div id="myChart" style="height:300px; width:500px;"></div> 
<button>Start Updates</button> 
</BODY> 
</HTML>

Nota: se vi interessa apporre modifiche in tempo reale al codice presente in questo articolo per poter fare delle prove senza dover editare niente sul proprio computer, potete trovare l’esempio scritto ed eseguibile su JSFiddle a questo link: http://jsfiddle.net/meccanismocomplesso/QAr4r/.

Caricando l’esempio sul Browser vedrete una situazione simile alla figura seguente (Fig.2), un grafico lineare dove tutti i punti hanno valore 0 sull’asse delle y e i tempi riportati nel formato ore-minuti-secondi  lungo l’asse x (i tempi certamente non corrisponderanno).

Fig.2: il grafico lineare come si presenta appena caricato

Questa è la situazione di partenza. In questo momento il grafico non sta acquisendo alcun dato. Per poter attivare l’acquisizione dei dati con il conseguente aggiornamento dobbiamo premere il pulsante in basso a sinistra “Start Updates”. Immediatamente il grafico comincerà a raffigurare un andamento via via con il passare del tempo (ogni 2 secondi viene generato un punto). Dopo un po’ di secondi avremo una situazione simile a quella raffigurata in Fig.3.

Fig.3: il grafico sta acquisendo i dati in real-time

Ma adesso passiamo ad analizzare in dettaglio il grafico e il codice JavaScript che lo genera.

Innanzitutto in questo esempio abbiamo scelto, per motivi di semplicità, di simulare il flusso di dati proveniente da una sorgente esterna, cioè i valori letti in continuazione da una lettura diretta di qualche grandezza, attraverso l’uso di una funzione che genera valori casuali che vanno da 0 a 1.

var y = Math.random();       
var x = (new Date()).getTime();

Quindi come variabile x abbiamo i tempi in cui avviene l’acquisizione, grazie alla funzione JavaScript getTime(). Mentre sulla y abbiamo utilizzato un’altra funzione JavaScript che genera valori casuali: Math.random().

Durante l’acquisizione dei dati, quest’ultimi vendono immagazzinati all’interno di un array data. Quindi in teoria, con il proseguire della lettura, questo array crescerebbe illimitatamente. Dato che per qualsiasi tipo di rappresentazione in real-time noi normalemente siamo interessati ad un determinato intervallo di tempo, è pressochè inutile immagazzinare infiniti valori numerici. Se questi venissero visualizzati il grafico diverrebbe sempre via via più piccolo risultando illeggibile. Se invece decidiamo di utilizzare solo gli ultimi n valori letti, perchè allora conservare valori che non verranno più visualizzati. Per questo motivo dobbiamo realizzare un buffer.

var t = 2000; 
//refresh time in millisec    
var x = (new Date()).getTime(); 
// current time    
var n = 20;  
//buffer size (sample number)    
var data = []; 
//buffer

Quindi definiamo con t l’intervallo tra una acquisizione e la successiva, con data il buffer e con n il numero di letture contenute nel buffer. Quando questo buffer sarà pieno, allora il valore più vecchio verrà cancellato per fare posto a quello appena acquisito. Questo ci permette di avere sempre una visualizzazione di n punti. L’ eliminazione dell’elemento più vecchio all’interno del buffer lo otteniamo applicando la funzione shift() direttamente sull’array data.

      if(data.length > n-1){
         data.shift();
      }

mentre per caricare il nuovo elemento utilizziamo la funzione push().

      data.push([x,y]);

Ogni volta che viene effettuata una nuova acquisizione è necessario aggiornare il grafico, e per fare ciò dobbiamo in realtà cancellare il grafico rappresentato, aggiornarne il contenuto e poi rappresentarlo nuovamente.

if (plot1) {          
    plot1.destroy();       
}       
plot1.series[0].data = data;       
options.axes.xaxis.min = data[0][0];       
options.axes.xaxis.max = data[data.length-1][0];       
plot1 = $.jqplot ('myChart', [data],options);

con destroy() distruggiamo il vecchio grafico, poi aggiorniamo l’oggetto jqplot plot1 con il nuovo buffer, e le options con i nuovi margini di tempo sull’asse x. Infine ridisegnamo il grafico, richiamando la funzione $.jqplot().

Con questo abbiamo terminato l’articolo.

Exit mobile version