Recentemente tive que desenvolver uma pequena aplicação, responsável por capturar dados de leituras de temperatura e umidade relativa de salas climatizadas e fazer um tratamento estatístico dos dados. O software responsável pela captura das leituras (que não foi escrito por mim, é proprietário) gera um arquivo CSV, com atualização de hora em hora, com todos os dados de todos os sensores.
A dificuldade estava em automatizar, de uma maneira prática e simples, a geração dos gráficos e estatísticas para os dados em questão. Decidi que usar uma interface web seria a melhor opção. Não requer nenhum software instalado no cliente (como seria o caso de usar essa ou aquela planilha de cálculo).
Passo a Passo
O primeiro passo é fazer a captura dos dados. Para isso, foi escrito um pequeno script ("captura"):
1 #!/bin/bash
2
3 cd /tmp
4 mount -t smbfs -o username=acervo,password=acervo //terminal/c /mnt/extra
5 mv /mnt/extra/dados/dado.csv /tmp
6 umount /mnt/extra
7 /opt/climatizacao/gera_sql /tmp/dados.csv | mysql climatizacao
8 rm /tmp/dados
9 rm /tmp/*.bak
Analizando linha a linha, começamos com a linha 4 que faz a conexão com o computador de aquisição de dados via samba. A linha 5 copia o arquivo de dados para o diretório temporário (ele na verdade move o arquivo. Assim, na próxima vez que o software for escrever novos dados, criará um novo arquivo, sem que aja a duplicação de dados). A linha 7 invoca o segundo script que trata os dados do arquivo CSV e insere dentro do MySQL.
O segundo passo é fazer o tratamento do arquivo CSV e injetar os dados no banco de dados. Isso é feito utilizando o segundo script ("gera_sql"), invocado na linha 7 do script "captura":
1 #!/bin/bash
2
3 dos2unix -a $1
4 sed -e s/\;//g $1 | sed -e 's|/| |' | sed -e 's|/| |' |\
awk {'print "insert into traineis values \
(\"\"\, \"20"$3"-" $1 "-"$2"\"\, \""$4 "\"\,
\""$8 "\"\, \""$9 "\"\, \""$10 "\"\, \""$11"\"); \n\
5 insert into exterior values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\,
\""$4 "\"\, \""$12 "\"\, \""$13 "\"\,
\""$14 "\"\, \""$15"\");\n\
6 insert into climatizada values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\,
\""$4 "\"\, \""$16 "\"\, \""$17 "\"\,
\""$18 "\"\, \""$19"\");\n\
7 insert into sala_3 values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\, \""$4 "\"\,
\""$20 "\"\, \""$21 "\"\, \""$22 "\"\,
\""$23"\");\n\
8 insert into audiovisual values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\, \""$4 "\"\,
\""$24 "\"\, \""$25 "\"\, \""$26 "\"\, \""$27"\");\n\
9 insert into armario values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\,
\""$4 "\"\, \""$28 "\"\, \""$29"\");\n\
10 insert into arcondicionado values
(\"\"\, \"20"$3"-" $1 "-"$2"\"\,
\""$4 "\"\, \""$30 "\"\, \""$31 "\"\,
\""$32 "\"\, \""$33 "\"\, \""$34"\");"'}
A linha 3 faz uma pequena conversão de formatos de arquivo texto (necessário dado o comando para quebra de linha ser diferente entre *NIX e Windows). A 4a. linha faz a separação dos dados (separados por vírgula). Mais do que isso, a 4a. linha faz ainda o tratamento da data (que é gerada do tipo dd/mm/aa e o SQL apenas reconhece aaaa-mm-dd). As linhas de 5 a 10 são as linhas de injeção de dados no SQL.
Injetados os dados no banco de dados, cabe ao aplicativo PHP fazer a leitura deles. Graças as funções estatísticas do MySQL, pode-se gerar rapidamente cálculos como média +- desvio padrão, máximos e mínimos, entre outros. A grande dificuldade está na geração do gráfico de umidade e temperatura.
Depois de muito pesquisar bibliotecas prontas para gerar gráficos em PHP (nenhuma me soou muito satisfatória), resolvi enfrentar o desafio de usar o GNUPlot (excelente ferramenta da qual já sou familiar há tempos) junto com o PHP.
Para isso, escrevi o programa graph.php, que passado o nome do sensor e as datas de início e fim, ele é capaz de gerar o gráfico de temperatura e umidade.
1 $sensor = $_GET['sensor'];
2 $limite1 = $_GET['limite1'];
3 $limite2 = $_GET['limite2'];
4 $size = $_GET['size'];
5 if (($limite1 != NULL) && ($limite2 != NULL))
6 $limite = "where data >= '$limite1' and data <= '$limite2'";
7 $filename = round(time() / 10 * rand(1,10));
8 $filename = "/tmp/".$filename;
9 if ($size == "small")
10 $term = "png size 640,480";
11 if ($size == "big")
12 $term = "png size 950,712";
13 $query = "select data, hora, temp, ur from $sensor $limite order by data, hora";
14 $result = mysql_query($query);
15 $rows = mysql_num_rows($result);
16 if ($rows > 720)
17 $passo = 10;
18 elseif ($rows > 360)
19 $passo = 5;
20 else
21 $passo = 1;
22 for ($i = $rows - 1; $i >= 0; $i = $i - $passo)
23 {
24
25 $x = mysql_result($result, $i, "data") . "-" . mysql_result($result, $i, "hora");
26 $y1 = mysql_result($result, $i, "temp");
27 $y2 = mysql_result($result, $i, "ur");
28 $command = "echo $x $y1 $y2 >> $filename";
29 exec ($command);
30 }
31 $command = "echo \"set ylabel \\\"Temperatura (celsius)\\\";
set y2label \\\"Umidade Relativa (%)\\\"; set xlabel \\\"Data\\\";
set grid; set y2tics; set term $term; set xdata time;
set timefmt \\\"%Y-%m-%d-%H:%M\\\";
plot \\\"$filename\\\" using 1:2 axis x1y1 title \\\"Temp\\\" with lines,
\\\"$filename\\\" using 1:3 axis x1y2 title \\\"UR\\\" with lines \" | gnuplot";
32 header("Content-type: image/png");
33 passthru($command);
34 $command = "rm $filename";
35 exec ($command);
As linhas de 1 a 4 fazem a leitura dos parâmetros. As linhas 7 e 8 geram um nome aleatório para o arquivo temporário de dados que será lido pelo GNUPLot. As linhas de 9 a 12 definem o tamanho do gráfico (em pixels). As linhas 13 e 14 fazem a conexão com o banco de dados. As linhas 16 a 21 fazem a triagem dos dados (dependendo do período que se deseja, o banco de dados possui mais de 3.000 pontos, o que deixava o programa um pouco lento. Como não havia necessidade de tantos pontos, o período foi dividido para que ficasse mais de acordo com o propósito do gráfico). As linhas de 22 a 30 fazem a leitura dos dados e a escrita deles no arquivo temporário. A linha 31 é o comando em sintaxe GNUPlot para que faça a formatação do gráfico, leitura dos dados e desenho dos pontos. A linha 32 envia ao navegador a informação que a saída será um arquivo PNG. A linha 33 faz a geração do gráfico (executa os comandos da linha 31). As linhas 34 e 35 apagam o arquivo texto temporário.
Para chamar o "grap.php", basta colocar em um arquivo HTML a chamada como se fosse um arquivo gráfico:
<img src=\"graph.php?sensor=$sensor&limite1=$limite1&limite2=$limite2&size=small\"
title=\"Gráfico de temperatura e umidade relativa para $sensor\" border=\"0\">
O resultado deste software é brilhante. Um gráfico gerado em segundos, extremamente leve (o gráfico mais pesado que gerei não passa de 15 kbytes).