前回 はセンサのデータをWEBアプリで受信できることを学びました。今回は受信したデータをグラフ表示できるようにしましょう。
matplotlib
matplotlib は Python でグラフ描画するライブラリです。使い方の詳細は別途勉強してください。グラフを表示するにはグラフィック機能が必要なので、Anaconda の Jupyter Notebook を使うと便利です。下の例ではセンサデータが二次元配列に保存された前提で、matplotlib を使ってグラフに表示するまでの過程を示しています。
横軸は最初のデータが入力されてからの経過時間(秒)にしています。その計算には datetime ライブラリを使用しています。
ソースコード
グラフを作る部分の動作が確認できましたので、WEBアプリに組み込んでみましょう。
- app.py
下のソースコードには環境に依存する部分があります。「筆者の環境(Apple Silicon Macbook)ではエラーが出るので、この2行を追加した。」の2行は不要なら削除してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# coding: utf-8 from flask import Flask, render_template, request, redirect, url_for import sqlite3 import datetime from io import BytesIO import urllib import matplotlib # 筆者の環境(Apple Silicon Macbook)ではエラーが出るので、 matplotlib.use('Agg') # この2行を追加した。 import matplotlib.pyplot as plt from matplotlib.backends.backend_agg import FigureCanvasAgg app = Flask(__name__) # データベースアクセスのためのグローバル変数 conn: sqlite3.Connection cur: sqlite3.Cursor DATABASE_NAME = 'SENSOR_DATA.db' # データベースに接続する def connect_database(): global conn, cur # データベースを作成して接続する(既に存在していれば再接続) conn = sqlite3.connect(DATABASE_NAME) cur = conn.cursor() # address_book というテーブルがまだ無ければ作る。 sql = 'CREATE TABLE IF NOT EXISTS sensor_data(\ id INTEGER PRIMARY KEY AUTOINCREMENT,\ time DATETIME2,\ temp FLOAT,\ humid FLOAT)' cur.execute(sql) conn.commit() # 初期画面:これまで受信したデータの表示 @app.route('/') def index(): global cur connect_database() cur.execute('SELECT * FROM sensor_data') data = cur.fetchall() return render_template('index.html', page='menu', sensor_data=data) # センサーデータの受信 @app.route('/post', methods=['GET']) def post(): # GETプロトコルでデータ受信 try: temp = str(request.args.get('temp', default=-999.0, type=float)) humid = str(request.args.get('humid', default=-999.0, type=float)) except Exception as e: return str(e) # データ管理のため現在時刻も保存する time = str(datetime.datetime.now()) # データベースに格納 global cur, conn connect_database() sql = 'INSERT INTO sensor_data(time,temp,humid) VALUES(?,?,?)' data = [time,temp,humid] cur.execute(sql, data) conn.commit() # 初期画面に遷移させる return redirect(url_for('index')) @app.route('/graph') def graph(): print('DEBUG: Reached to graph()') return render_template('index.html', page='graph') @app.route('/plot') def plot(): print('DEBUG: Reached to plot()') global cur connect_database() cur.execute('SELECT * FROM sensor_data') data = cur.fetchall() time = [] temp = [] humid = [] start_time = datetime.datetime.strptime(data[0][1], '%Y-%m-%d %H:%M:%S.%f') for d in data: t = datetime.datetime.strptime(d[1], '%Y-%m-%d %H:%M:%S.%f') delta = (t - start_time).seconds time = time + [delta] temp = temp + [float(d[2])] humid = humid + [float(d[3])] # グラフの描画 fig = plt.figure() plt.plot(time, temp) plt.plot(time, humid) #plt.scatter(time, temp, humid) # 離散グラフならこちらを有効にする plt.show() canvas = FigureCanvasAgg(fig) png_output = BytesIO() canvas.print_png(png_output) img_data = urllib.parse.quote(png_output.getvalue()) return img_data if __name__ == '__main__': app.run(debug=True) |
グラフ関連およびそのデータを転送するためのライブラリが必要なので import 文が増えています。graph() はグラフの画面を表示するために存在します。plot() の中でグラフを図形データとして作成し、BytesIOライブラリを使ってブラウザに返しています。
- index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<!doctype html> <html lang="ja"> <head> {% if page == "menu" %} <meta http-equiv="refresh" content="1; URL="> {% endif %} <title>Flask Sample</title> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript"> function drawGraph() { // Pythonプログラムを呼んでグラフデータを取得し、plotimgに挿入する。 var plotdata = document.getElementById('plotimg'); $.get("/plot", function(data) { plotdata.src = "data:image/png:base64," + data; }); }; $(document).ready(function() { // ここはブラウザを立ち上げたときに毎回実行される。 drawGraph(); }); </script> </head> <body> <h2>Flaskによるセンサ・データの受信テスト</h2> {% if page == "menu" %} <a href="/graph"><button>グラフを表示する</button></a></br> <table border="1"> <th>No.</th><th>時刻</th><th>温度</th><th>湿度</th> {% for record in sensor_data %} <tr> <td>{{ record[0] }}</td><td>{{ record[1] }}</td><td>{{ record[2] }}</td><td>{{ record[3] }}</td> </tr> {% endfor %} </table> {% elif page == "graph" %} <img id=plotimg></img></br> <a href="/"><button>戻る</button></a> {% endif %} </body> </html> |
テスト
プログラムを動作させ、前回やったようにシェルスクリプトからセンサデータに見立てた情報を送信します。
- senddata.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#! /bin/bash URL="http://localhost:5000/post" curl "$URL?temp=20.0&humid=50.0" read -p "Hit Enter key." curl "$URL?temp=20.2&humid=51.3" read -p "Hit Enter key." curl "$URL?temp=20.3&humid=52.8" read -p "Hit Enter key." curl "$URL?temp=20.6&humid=55.1" read -p "Hit Enter key." curl "$URL?temp=20.7&humid=57.8" |
コメント