SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|12|
2025|01|

2025-01-25(Sat) DBusで数当てゲーム

  まー、保険タイプの仕事つうのは、何も起こらなければ、何もしないでチャリンチャリンなわけだが、まっとうな神経を持っていれば、だからこそ客に何か起こった時には、イザという働きをしなければな、と構えているべきである。

  で、起きた。決して、事前にアレコレしていたワケではないので、決してベストな体勢ではないが、そこからは誠心誠意やらせてもらう。どうもDBusがカラんでいるらしい。GUIを下支えする何か、という程度の認識だったが、この機に学んでおくか。なんだか面白そうだし。というわけで、基本的な概念について、学んでみることにした。自分にとって学ぶとは、実際に使ってみることであり、可能な限りそれを何かに役立ててみることである。

  DBusの基本については、ネット上にそれなりの情報があるが、ごく簡単にまとめると以下だ。まず、バスという道路がある。1本のシステムバスと、ユーザ毎のセッションバス。道路の脇にはサービスという店舗が並んでいて、オブジェクトパスという看板が掲げられている。店舗にはインターフェイスという職人がいて、メソッドという仕事を受け付ける。

  実例を挙げると以下。

$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
    /org/mate/ScreenSaver \
    org.mate.ScreenSaver.GetActiveTime
 
   uint32 60

  セッションバスという道路脇の、org.mate.ScreenSaverというサービスの店舗の、/org/mate/ScreenSaverというオブジェクトパスの看板を見て、org.mate.ScreenSaverというインターフェイスの職人に、GetActiveTimeというメソッドの仕事を依頼したら「uint32 60」という答えを返してくれた、という感じ。つまり、スクリーンセーバ起動までの時間は60秒って知れたわけだ。

  次はこれ。

$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
    /org/mate/ScreenSaver \
    org.mate.ScreenSaver.SetActive boolean:true

  同じ職人に、SetActiveという仕事を依頼すれば、スクリーンセーバを起動してくれる。

  なお、すべての店舗には共通にorg.freedesktop.DBus.Introspectableというインターフェイスの職人がいて、Introspectというメソッドの仕事を依頼できる。依頼すると、自分の店舗の職人と仕事のリストを返してくれる。

$ dbus-send --session --dest=org.mate.ScreenSaver --print-reply --type=method_call \
    /org/mate/ScreenSaver \
    org.freedesktop.DBus.Introspectable.Introspect
 
   :
  <interface name="org.mate.ScreenSaver">
     :
    <method name="GetActiveTime">
      <arg name="seconds" direction="out" type="u"/>
    </method>
    <method name="SetActive">
      <arg name="value" direction="in" type="b"/>
    </method>
     :

  DBusを通じて、既存の機能に命令を送るという基本は以上だが、それだけでは片手落ち。クライアント処理だけでなく、サーバ側を作ってみないと。

  自分はサンプルを作るにしても、何らかの意味を持たせることで学びが深まると思っているので、今回は古典的な「数当てゲーム」を実装してみた。自分にとって「数当てゲーム」は往年のBASIC言語における「hello, world」だ。幾度となく空で打ち込んで動かして遊んだのがコンピュータとの原体験。

  というわけで、以下がRubyのdbusモジュールによる「数当てゲーム」サーバの実装である。

#!/usr/bin/env ruby
 
require 'bundler/setup'
require 'dbus'                                                  # bundle add ruby-dbus
 
class DBusKazuate < DBus::Object
 
    @@range = 10
    @@answer = nil
 
    # プレイヤ用のインタフェイス
    dbus_interface('jp.itline.test.Kazuate') {                  # Interface
 
        # ゲームの開始メソッド
        dbus_method(:ready, 'out res:s') {                      # Member
            @@answer ||= (rand * @@range).to_i + 1
            "I'm thinking of a number between 1 and %d." % @@range
        }
 
        # 予想した数へ対応メソッド
        dbus_method(:try, 'in guess:i, out hint:s') {|guess|    # Member
            unless(@@answer)
                'Not ready yet...'
            else
                if(guess < @@answer)
                    'Too low!'
                elsif(guess > @@answer)
                    'Too high!'
                else
                    @@answer = nil
                    'Congratulations!'
                end
            end
        }
    }
 
    # ゲームマスタ用のインタフェイス
    dbus_interface('jp.itline.test.Kazuate.master') {           # Interface
 
        # 数の範囲の変更メソッド
        dbus_method(:range, 'in range:i, out res:s') {|range|   # Member
            @@range = range
            'Range is set.'
        }
 
        # 答えを見るメソッド
        dbus_method(:answer, 'out ans:i') {                     # Member
            @@answer || -1
        }
    }
end
 
# DBus のセットアップ
bus = DBus::SessionBus.instance                                         # Bus is session (not system)
bus.request_name('jp.itline.test.Kazuate')                              # Service (for --dest)
bus.object_server.export(DBusKazuate.new('/jp/itline/test/Kazuate'))    # Object path
DBus::Main.new.tap {|main|
    main << bus
}.run

  実行すると、サーバとして待ち受けを開始するので、クライアントとしてゲームに参加する。クライアントアプリは、上記と同じdbus-send。

$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
	/jp/itline/test/Kazuate \
	jp.itline.test.Kazuate.ready
 
   string "I'm thinking of a number between 1 and 10."

  1から10までのどれでしょう? となるので、

$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
	/jp/itline/test/Kazuate \
	jp.itline.test.Kazuate.try int32:5
 
   string "Too low!"

  5ですか? と、答えると、それは小さすぎ、となる。

$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
	/jp/itline/test/Kazuate \
	jp.itline.test.Kazuate.master.answer
 
   int32 8

  上記のコードを見れば明らかだが、ゲームマスタ用に答えを見るメソッドが用意されているので、やってみると8であることがわかる。

$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
	/jp/itline/test/Kazuate \
	jp.itline.test.Kazuate.try int32:8
 
   string "Congratulations!"

  8を答えれば正解だ。

$ dbus-send --session --dest=jp.itline.test.Kazuate --print-reply --type=method_call \
	/jp/itline/test/Kazuate \
	org.freedesktop.DBus.Introspectable.Introspect
 
   :  
  <interface name="jp.itline.test.Kazuate">
    <method name="ready">
      <arg name="res" direction="out" type="s"/>
    </method>
    <method name="try">
      <arg name="guess" direction="in" type="i"/>
      <arg name="hint" direction="out" type="s"/>
    </method>
  </interface>
  <interface name="jp.itline.test.Kazuate.master">
    <method name="range">
      <arg name="range" direction="in" type="i"/>
      <arg name="res" direction="out" type="s"/>
    </method>
    <method name="answer">
      <arg name="ans" direction="out" type="i"/>
    </method>
  </interface>
</node>"

  やはり、IntrospectableのIntrospectを実行すれば、リストを返してくれる。

  つうわけで、DBusはGUIを下支えする何か、という程度の認識だったが、GUIに限らず、なかなか便利に使えそうな機構である。ややオープンする記述は面倒だが、TCPと違って通信内容の解釈が不要で、関数コールライクなのは利点だ。

  続いて、クライアントや通信キャプチャを試してみたい。