あっかぎのページ

データをWebグラフ化したときのメモ

20150325_1

とあるデータをグラフで見たいなーと思ってプログラムしたときのメモ。

使った技術

  • Ruby
  • sinatra
  • slim
  • sqlite3
  • jquery
  • jqplot
  • crontab
  • ajax

お題

Raspbery PiのCPU温度を定期的に取得してグラフ表示。

やり方

  1. Raspberry PiのCPU温度を取得するプログラム(cpu.rb)
  2. crontabでcpu.rbを定期的に取得
  3. sinatraでWeb表示(myapp.rb)
  4. jqPlotを利用してグラフ表示

Raspberry PiのCPU温度を取得するプログラム(cpu.rb)

Raspberry Piは/sys/class/thermal/thermal_zone0/tempを見るとCPU温度がわかります。例えば、こんな感じ。

$ cat /sys/class/thermal/thermal_zone0/temp
40084

この場合、40.084℃ということになります。なので、1000で割るとちょうど℃に変換できますね。

今回はプログラムを実行したときの「時刻」と「温度」をデータベースに記録することにします。使うデータベースはsqlite3です。

CPU温度を取得するプログラム(cpu.rb)

#!/usr/bin/env ruby

require 'sqlite3'

df = '/home/pi/a/cpu/cpu.db'

time = Time.now.strftime("%Y-%m-%d %H:%M:%S")
temp = open('/sys/class/thermal/thermal_zone0/temp'){|f| f.read }.to_f / 1000
puts time + " " + temp.to_s

db = SQLite3::Database.new(df)
sql = <<-SQL
        CREATE TABLE cpu(
                time    text,
                temp    real
        )
SQL
# テーブル無ければ作成
unless db.execute('select tbl_name from sqlite_master').flatten.include?('cpu')
        db.execute(sql)
end

time = Time.now.strftime("%Y-%m-%d %H:%M:%S")
db.execute('INSERT INTO cpu(time, temp) VALUES(?,?)', time, temp)
db.close

上ではデータベースファイルをdf = '/home/pi/a/cpu/cpu.db'で定義しています。プログラムの前半で標準出力へ出力しています。中盤以降はデータベースへ記録するための記述になります。時刻はtext,温度はreal(実数)の記録です。

次のコマンドで実行するとこんな感じの出力になります。

$ ruby cpu.rb
2015-03-29 16:36:36 40.084

続けてデータベースの確認です。sqlite3でファイルを読み込んで、sqlite>に続けてコマンドを入力していきます。

$ sqlite3 /home/pi/a/cpu/cpu.db
sqlite> .schema
CREATE TABLE cpu(
                time    text,
                temp    real
        );
sqlite> select * from cpu;
2015-03-29 16:36:36 40.084
sqlite> .quit

ここでは、データベースの形式確認(.schema)とデータ確認(select * from cpu;)をして終了(.quit)しています。

一連の操作で問題がなければCPU温度をデータベースへ記録する準備が整いました。

crontabでcpu.rbを定期的に取得

プログラムでCPU温度を記録できるようになったので、これを定期的にプログラムを動作させて定期的にCPU温度を取得するようにします。

これを行うにはcronというツールが最適です。cronは記載された内容を定期的に実行するツールです。具体的にはcrontab -eとすることで定期実行を行う記述が書けます。

$ crontab -e
*/10 * * * * ruby /home/pi/a/cpu/cpu.rb # 10分おきにcpu.rbを実行

crontabの書き方についてはこちらを参照しました。crontabの書き方

これで10分おきに自動でCPU温度をデータベースへ記録するプログラムが実行されるようになりました。

sinatraでWeb表示

sinatraはrubyで動くwebフレームワークです。例えば、こんな感じでhello.rbを記載します。

require 'sinatra'

get '/' do
  "hello world"
end

これを実行します。

$ ruby hello.rb

実行してブラウザで”localhost:4567″にアクセスすると”hello world”と表示されます。たったこれだけの記述でWebサーバーになります。すごい!詳細はこちらのsinatra 日本語ページをご参照ください。このsinatraを利用して先ほどのCPU温度をブラウザで表示できるようにします。

ここでは主に2つのことを行います。

  1. データ表示
  2. データベースからデータ取得

1つ目はアクセスに対してページを返します。2つ目はデータベースのデータを取得してそれを返します。これをプログラムで書くと(myapp.rb)

#!/usr/bin/env ruby

require 'sinatra'
require 'sinatra/reloader'
require 'sqlite3'
require 'slim'
require 'json'

set :bind => '192.168.11.100'
set :port => 9999

get '/' do
    slim :index
end

get '/data' do
    db = SQLite3::Database.new('cpu.db')
    data = db.execute('SELECT * FROM cpu')
    data.to_json
end

ここでは、slimというHTMLを簡略化して記述できるものを利用しています。

sinatra,sqlite3,slimの関連ファイルを読み込んでいます。さらに、ajaxという手法を利用してデータベースのデータを取得するためにjsonも最初に読み込みます。また、IPアドレスとPORT番号を自分の環境に合わせて設定しています。ここではブラウザで192.168.11.100:9999にアクセスすることで表示できるように設定しています。

最初のget '/'でページにアクセスしslim形式のindex.slimの内容を返しています。get '/data'はajaxという手法でデータベースのデータ(時刻とCPU温度)をjson形式で返しています。ajaxを利用してやりとりすることで、アクセスした時間に応じたデータを取得することが可能になります。(アクセスする度に動的にデータを生成することができます)

sinatraはviews以下のフォルダに表示用のファイルを置きます。ここではindex.slimをviews以下に置くことにします。index.slimの中身はこんな感じです。

doctype html
html
    head
        title cpu thermo data
        meta charset='utf-8'
    body
        #chartdiv style='height:400px;width:960px;'
        script src='jquery.min.js'
        script src='jquery.jqplot.min.js'
        script src='plugins/jqplot.dateAxisRenderer.min.js'
        link rel='stylesheet' href='jquery.jqplot.min.css' type='text/css'

javascript:
    $(function(){
        $.ajax({
            type:       'GET',
            url:        '/data',
            dataType:   'json',
        }).done(function(d){
            $.jqplot('chartdiv', [d], {
                axes:{
                    xaxis:{
                        renderer: $.jqplot.DateAxisRenderer,
                    }
                },
                seriesDefaults:{
                    showMarker: false,
                }
            });
        });
    });

インデントで構造を表現することでタグを省略して簡略化してHTMLの記述ができます。requireでslimを読み込むことでsinatraはこれをHTMLに変換してブラウザにデータを返します。

本質的には#chartdiv style='height:400px;width:960px;'でグラフ領域を指定して、後述のjqPlotを呼び出してグラフ表示しています。

javascript部はajaxというテクニックで動的にデータを生成します。ここでは、’/data’へGETリクエストして、sinatraから帰ってきたjsonデータを取得します。取得したデータをjqplotへ渡してグラフ表示します。こうすることで、アクセスする度に最新のデータをグラフ化することができるようになります。

jqPlotを利用してグラフ表示

jqPlotというグラフツールを利用します。

使い方はこんな感じ。

  • jquery,jqplotのjs,cssの読み込み
  • グラフ領域の確保(body内にid指定)
  • $.jqplotにて描画
<!DOCTYPE html>
<html lang="ja">
<head>
        <meta charset="UTF-8">
        <title>jqPlot</title>
</head>
<body>
<div id="chartdiv" style="height:400px;width:300px; "></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="jquery.jqplot.min.js"></script>
<link rel="stylesheet" type="text/css" href="jquery.jqplot.css" />
<script>
$.jqplot('chartdiv',  [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]);
</script>
</body>
</html>

20150325_2

今回はデータベースから時刻データを取得するのでplugins/jqplot.dateAxisRenderer.min.jsを読み込んで時刻対応できるようにしています。

ファイル構成

  • cpu.rb
  • myapp.rb
  • public
    • jquery.min.js
    • jquery.jqplot.min.js
    • jquery.jqplot.min.css
    • plugins/jqplot.dateAxisRenderer.min.js

※views/index.slimが無いのはmyapp.rbにひとまとめしたためです。githubにも公開しています

myapp.rbにindex.slimを記述版

#!/usr/bin/env ruby

require 'sinatra'
require 'sinatra/reloader'
require 'sqlite3'
require 'slim'
require 'json'

set :bind => '192.168.11.100'
set :port => 9999

get '/' do
    slim :index
end

get '/data' do
    db = SQLite3::Database.new('cpu.db')
    data = db.execute('SELECT * FROM cpu')
    data.to_json
end

__END__

@@ index

doctype html
html
    head
        title cpu thermo data
        meta charset='utf-8'
    body
        #chartdiv style='height:400px;width:960px;'
        script src='jquery.min.js'
        script src='jquery.jqplot.min.js'
        script src='plugins/jqplot.dateAxisRenderer.min.js'
        link rel='stylesheet' href='jquery.jqplot.min.css' type='text/css'

javascript:
    $(function(){
        $.ajax({
            type:       'GET',
            url:        '/data',
            dataType:   'json',
        }).done(function(d){
            $.jqplot('chartdiv', [d], {
                axes:{
                    xaxis:{
                        renderer: $.jqplot.DateAxisRenderer,
                    }
                },
                seriesDefaults:{
                    showMarker: false,
                }
            });
        });
    });

おわりに

今回のやり方で動的にデータをグラフ化することができるようになりました。実は「資産状況をグラフ化したいなー」ということでいろいろ試行錯誤してこのメモ内容に行きつきました。ここの内容でWebの基本を押さえることができたと思うので、これをベースにいろいろとできることを増やしていきたいなと思っています。

参考