2016年2月7日日曜日

【Ruby】 ncurseswを使って常駐型アプリ

今回はncurseswを使って常駐型アプリっぽいものをつくってみたのでその紹介です。図1は実行中の様子です。

図1 実行中の様子



動作環境

以下の環境で動作を確認しました。
  • Ubuntu 14.04
  • Ruby 2.2.0
  • ncursesw(gem) 1.4.9

この環境で必要なものをインストールするコマンドは以下に示す通りです。まあncurseswが使える環境であれば動くと思います。
$ sudo apt-get install libncursesw5-dev
$ gem install ncursesw

ソースコード
https://github.com/seinosuke/debug_room
examples/ 以下の main.rb で本体が起動します。


主な使い方

矢印キーの上下で左側のメニューウィンドウから使いたい機能を選択し、Enterキーを押します(まあそんな選ぶほど無いですが...)。アプリケーションの終了はqキーでもできます。

  • Calendar ←→ Clock カレンダーと時計とで表示が切り替わる
  • Mini Game 避けゲーが始まる(図2参照)
  • Exit 終了する

図2 ミニゲームの様子

ミニゲームは降ってくる弾を上下左右の矢印キーを使って避けるというゲームになっています。また、各機能に影響はありませんが、0時から7時までの間マスコットは寝ます。


CPU使用率監視

このアプリは実行中、CPU使用率を監視していて、その結果に応じてマスコットの動きが変化します。図3は分割された上のウィンドウで何か重い処理を流しているときの様子です。

図3 CPU使用率が高いときの様子

application.rb中の以下に示す箇所が監視を行っているメソッドです。
    # cpu使用率を監視するスレッド
    def watch_cpu_thread
      Thread.new do
        stat01 = stat_result
        stat02 = stat_result

        until @main_screen.exit
          stat01 = stat02
          sleep 1
          stat02 = stat_result

          @main_screen.mascot.on_busy = stat01.zip(stat02).map do |cpu01, cpu02|
            sub = cpu02.zip(cpu01).map { |a, b| a - b }
            total = sub.inject(:+).to_f
            sub.each_with_object(total).map(&:/)
          end.map { |cpu| cpu[3] < 0.05 }.any?
        end
      end
    end

    # watch_cpu_thread で使われる /proc/stat の情報を返す
    def stat_result
      `cat /proc/stat`.split("\n").select do |line|
        line.match(/^cpu\d+/)
      end.map do |cpu|
        cpu.split[1..4].map { |v| v.to_i }
      end
    end

どのような基準でCPU使用率が高いと判断するか、そもそもCPU使用率の計算はどのようにするかについて悩みました。
結局 /proc/stat から各CPUの情報を取得してきて、どれかひとつでもタスク待ちの割合が5%を切ったらビジーであると判断するようにしました。
環境が違ったりするとここで正常に動かなくなるので、基準の設定も含めて環境に応じて適宜書きなおしてもらえたらと思います。 @main_screen.mascot.on_busy を true にすると図3のような状態になります。



何かしゃべらせる

examples/ 以下の main.rb が動作している状態で、他から同じく examples/ 以下の client.rb を実行すると図4のようにふきだしにセリフが表示されます。

$:.unshift File.expand_path '../lib', File.dirname(__FILE__)
require "debug_room"

# 何らかの処理...
sleep 3

client = DebugRoom::Client.new
client.send_message("終わったよ")

図4 何かしゃべらせたときの様子

20000番ポートでTCPサーバをたてています(先程と同じファイル application.rb にてポート番号を指定しています)。何か長い処理の終了を知らせる、あるいは新着メールなどを監視させてその結果を表示させるという具合に組み合わせるとおもしろいと思います。


必要コンソールサイズなどについて

実行するためには少なくとも21行76列のコンソールサイズが必要です。また、画面の大きさに対して柔軟性はなく、マスコットなどの大きさは完全に固定です。位置もウィンドウ右下で固定ですが、これは図5のように完全に透過した別のターミナルと重ねることでデスクトップマスコットっぽさを出すためという意図があります。

図5 使い方の例


おわりに

完全に自分で楽しむ用につくりました。次があればその時はもっと普通の方法でデスクトップマスコットをつくってみたいです。

参考サイト
proc/statを使ってcpu使用率を計算する
https://github.com/sup-heliotrope/ncursesw-ruby
NCURSES Programming HOWTO
せりか式 ncurses

0 件のコメント:

コメントを投稿