サーバーにある温度データを取得し、Chart.jsを使ってグラフを作成する

 

過去の関連記事

ESPr Developerで温度測定し、レンタルサーバー上のSQLiteにデータ蓄積します。サーバーとHTTP通信を行い蓄積したデータを取得し、Chart.jsを使ってグラフ出力する。
今回はこれら一連の処理の最終回となります。過去の関連投稿は下記となりますので、よろしければご確認下さい。

サーバー上のデータをグラフ化

レンタルサーバー上のSQLiteに蓄積したデータを取得しChart.jsを使ってグラフ出力します。結果は次の1つ目の図の通りです。先回、温度データを直接プログラム内に書き込んだものと外観的に違いはありませんが、温度データはサーバー上のSQLiteファイルからHTTP通信を使って取得しています。
また、2つ目の図は、サーバー上のPHPプログラムで発生させたランダムデータとブラウザー上で発生させたものをグラフに反映させた様子です。あまり深い意味はないのですが、速度の差を体感できるかなと思って作ってみました。当然のことながら、すごく遅いというほどではありませんが、サーバー側で処理させると少し間があく感じがします。
ボタンは3つあり、それぞれに処理内容を書いていますので、説明は省略させて頂きます。

3日間の温度測定結果   “101.html”  ※リンク有

サーバー処理、ブラウザ処理によるランダムデータグラフ化

なぜか、サーバーから温度データを取得し、グラフに反映させると縦軸のスケールが変わってしまいます。少し気になりますが、今のところそれほど必要性がないので、今のところ見なかったことにさせて頂きます。一応、予定していたことはできたので、次にプログラムを紹介して今回は完了にします。

プログラム

(1)ブラウザ側プログラム(HTML+JavaScript)
行番13~24は、HTTP APIを設定する関数,行番27~55はサーバー側のPHPプログラムを起動し、温度データ(もしくはランダムデータ)を取得し、配列変数に設定後、Chart.jsに反映させる関数です。サーバー側のPHPプログラムは2つあり、1つはランダムデータを取得するプログラム、もう1つは測定温度を取得する関数です。行番74と行番78でサーバー側のPHPを起動するボタンを形成し、このボタンがクリックされると、それぞれ行番172~174,行番176~178が応答し、先ほどの関数(行番27~55)を呼び出します。この時引き数として“prg_no”が関数に渡されますが、行番34~38でこの引き数の値によって、呼び出すサーバー側のプログラムを変えています。行番30~52はループになっていますが、1日分づつデータを取得し3回繰り返すようになっています。サーバーから取得したデータは、“[ ]”で囲まれた “ , ” 区切りの文字列ですので、行番46で不要文字を無くし、配列に格納します。行番31でループ数を文字にし、行番41,42でサーバー側に送る為の処理をしています。サーバー側では温度測定データを取得する場合、このループ回数に応じて対象日付判定し、温度データ取得後、ブラウザーに返信します。行番48~50では、配列に格納された値をChart.jsに反映させる為にデータの入れ替えを行っています。
行番161~170では、ブラウザ側の乱数ボタンがクリックされた時に乱数を発生する処理です。“Chart.js”に直接関連する部分は先回投稿と重複しますので省略させて頂きます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">

	<title>サーバーから温度測定結果取得</title>
	<script src="js_lib/Chart.bundle.js"></script>
	<script src="js_lib/utils.js"></script>

   	<script type="text/javascript">

	    // HTTP通信API設定
	    function createXmlHttpRequest(){
    	        var xmlhttp=null;
    	        if(window.ActiveXObject){
        	    try { xmlhttp=new ActiveXObject("Msxml2.XMLHTTP"); }
        	    catch(e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
            		   catch (e2){ }
                    }
    	        }else if(window.XMLHttpRequest){
        	    xmlhttp = new XMLHttpRequest();
    	        }
    	        return xmlhttp;
	    }

	    // サーバー側PHPプログラムを起動し、温度測定結果を得る
	    function exec_svr_prg(prg_no){

	    	var rtn_all = "" ;
		for (var j = 0 ; j < 3 ; j = j + 1 ){
		    var sch_inf = String(j) ;
    	            var xmlhttp = createXmlHttpRequest();
    	            if(xmlhttp != null){
			if(prg_no == "101"){
        	    	    xmlhttp.open("POST", "101_*****.php", false);
        	    	}else{
			    xmlhttp.open("POST", "102_*****.php", false);
			}
			
			xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") ;
        	    	var data = "data="+sch_inf;
        	    	xmlhttp.send(data);
        	    	var rtn = xmlhttp.responseText ;
	    		rtn_all = rtn_all + rtn ;

			var rtn_Ary = rtn.replace("[","").replace("]","").replace(" ","").replace(" ","").split(",") ;

			if(j == 0) { for (var i=0 , len = rtn_Ary.length ; i < len ; ++i ){ data_00[i] = rtn_Ary[i] ; } }
			else if(j == 1) { for (var i=0 , len = rtn_Ary.length ; i < len ; ++i ){ data_01[i] = rtn_Ary[i] ; } }
			else if(j == 2) { for (var i=0 , len = rtn_Ary.length ; i < len ; ++i ){ data_02[i] = rtn_Ary[i] ; } }
		    }
	        }

		window.myLine.update();
	    }

    </script>

	<style>
	canvas{
		-moz-user-select: none;
		-webkit-user-select: none;
		-ms-user-select: none;
	}
	</style>
</head>

<body>
	<div style="width:75%;">
		<canvas id="canvas"></canvas>
	</div>
	<br>

	<button id="randData_Svr">乱数(サーバー処理(PHP))</button>
	<button id="randomizeData">乱数(ブラウザ処理(JavaScipt))</button>

	<br><br>
	<button id="get_Temp_Svr">温度測定結果取得(サーバー処理(PHP))</button>
	<!--
	<form>
		<input type="button" name="btn" id="btn01" onClick="exec_svr_prg('102');" value="温度測定結果取得(サーバー処理(PHP))">
	</form>
	-->

	<script>

		// 18~28の乱数を生成
		var randomScalingFactor = function() {
			return 18 + Math.random() * 10;
		};

		// 初期データ設定
		var data_00 = [15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15];
		var data_01 = [16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16];
		var data_02 = [17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17];

		var config = {
			type: 'line',
			data: {
				labels: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' , '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
				datasets: [{
					label: '11月19日(月)',
					data: data_00,
					borderColor: window.chartColors.red,
					backgroundColor: 'rgba(0, 0, 0, 0)',
					fill: false,
					lineTension: 0
				}, {
					label: '11月20日(火)',
					data: data_01,
					borderColor: window.chartColors.blue,
					backgroundColor: 'rgba(0, 0, 0, 0)',
					fill: false,
					lineTension: 0
				}, {
					label: '11月21日(水)',
					data: data_02,
					borderColor: window.chartColors.green,
					backgroundColor: 'rgba(0, 0, 0, 0)',
					fill: false,
					lineTension: 0
				}]
			},
			options: {
				responsive: true,
				title: {
					display: true,
					text: '3日間の温度測定結果'
				},
				tooltips: {
					mode: 'index'
				},
				scales: {
					xAxes: [{
						display: true,
						scaleLabel: {
							display: true,
							labelString: '時刻 [時]'
						}
					}],
					yAxes: [{
						display: true,
						scaleLabel: {
							display: true,
							labelString: '温度 [℃]'
						},
						ticks: {
							suggestedMin: 15,
							suggestedMax: 30,
						}
					}]
				}
			}
		};

		window.onload = function() {
			var ctx = document.getElementById('canvas').getContext('2d');
			window.myLine = new Chart(ctx, config);
		};

		document.getElementById('randomizeData').addEventListener('click', function() {
		    for (var j = 0 ; j < 3 ; j = j + 1 ){
			for (var i = 0, len = data_00.length; i < len; ++i) {
			    if(j == 0) { data_00[i] = randomScalingFactor(); }
			    else if(j == 1) { data_01[i] = randomScalingFactor(); }
			    else if(j == 2) { data_02[i] = randomScalingFactor(); }
			}
		    }
			window.myLine.update();
		});

		document.getElementById('randData_Svr').addEventListener('click', function() {
			var rtn = exec_svr_prg('101');
		});

		document.getElementById('get_Temp_Svr').addEventListener('click', function() {
			var rtn = exec_svr_prg('102');
		});

	</script>
</body>

</html>

(2)サーバー側 温度データ取得プログラム(PHP)
行番7でブラウザ側からのループ回数を受け取ります。この数値によって、行番9~11で設定する対象日付を確定します。ここでは元データがこの3日分しかありませんので、この様にしていますが、最初は当日を起点とし1日前,2日前などを日付演算で求める計画でした。
行番17~37は、1時間ごとの温度測定結果を得るために24回ループし、データを取得しています。行番18,19はSQL文のwhere句でに設定する対象の開始と終了時刻を求めています。SQL実行後、対象時間範囲の最初(最も早い時刻)のデータを取得後、行番35 breakによって次の時間帯のループに移ります。この様にして、24時間分のデータを取得し、行番40でブラウザー側に結果を返信します。
今回、24回ループする様な処理をしていますが、SQLの達人であれば、SQL文のみで24時間分の結果を1回で取得できるのかもしれません。今後の課題です。

<?php

error_reporting(0) ;
mb_language("ja") ;
mb_internal_encoding("UTF-8") ;

$num_dat=intval($_POST["data"]) ;

$tmp_dt[0]="2018-11-19";
$tmp_dt[1]="2018-11-20";
$tmp_dt[2]="2018-11-21";

$db = null;$sql = null;
$db = new SQLite3("../***/**/temp_record.sqlite3");

$get_data = "";
for($j = 0 ; $j < 24; $j++){
	$start_tm = $tmp_dt[$num_dat]." ".mb_substr("00".$j,-2).":00:00" ;
	$end_time = $tmp_dt[$num_dat]." ".mb_substr("00".$j,-2).":59:59" ;

	$res = null;
	$sql = "SELECT g_temp ";
	$sql = $sql."FROM temp_reco ";
	$sql = $sql."WHERE created_datetime >= '".$start_tm."' AND created_datetime < '".$end_time."' ";
	$sql = $sql."ORDER BY created_datetime";
	$res = $db->query($sql);

	while($row = $res->fetchArray(SQLITE3_NUM)) {
	    $get_tmp = $row[0] ;
    	    $get_data = $get_data.$get_tmp ;

	    if($i != 23){
		$get_data = $get_data."," ;
	    }
	    break ;
	}
}


echo "[".$get_data."]" ;

?>

(3)サーバー側ランダムデータ取得プログラム(PHP)
サーバー側でランダムデータを発生させるプログラムは次の通りです。説明は省略させて頂きます。

<?php

error_reporting(0);
mb_language("ja");
mb_internal_encoding("UTF-8");

$num_dat=$_POST["data"];

$rtn_data = "";
for($i=0; $i<24; $i++){
	$tmp_rnd = mt_rand(180,280) / 10;
	$rtn_data = $rtn_data.$tmp_rnd ;
        
 	if($i != 23){
		$rtn_data = $rtn_data."," ;
	}
}

echo "[".$rtn_data."]" ;

?>

まとめ

一応、思っていた様なことは出来ました。何か面白うそうなテーマがあれば、連続監視モニターの様なものにチャレンジしてみたいと思います。

★今回の成果★ ※リンクしています。
3日間の温度測定結果   “101.html

※サーバー環境:レンタルサーバー(ロリポップ)
ライトプラン(月々250円~)