Главная страница

D-Bus клиент с помощью Ruby

17:46, 2008-06-22 [ ru ]

Продолжаю переводить руководство по D-Bus для рубистов (и не только). Всё добро хранится в Git (git clone git@gitorious.org:ruby-dbus-doc-russian/mainline.git) и ждет патчей от знатоков русского языка (коим я не являюсь).

D-Bus клиент с помощью Ruby

Эта глава описывает создание проcтого D-Bus клиента c помощью Ruby и включает следующие темы:

  1. Использование библиотеки
  2. Присоединение к шине
  3. Вызов метода 3.1. Интроспекция
  4. Асинхронный вызов метода
  5. Ожидание сигнала
  6. Еще немного об интроспекции 6.1 Обход дерева объектов

Использование библиотеки

Если вы хотите использовать библиотеку, вы должны включить её в вашу программу с помощью:

require 'dbus'

Это всё! Теперь мы можем перейти к использованию…

Присоединение к шине

Обычно в системе запущено две шины - шина сессии и системная шина. Доступ к системной шине может быть получен следующим образом:

bus = DBus::SystemBus.instance

Вероятно вы уже догадались каким образом получить доступ к шине сессии:

bus = DBus::SessionBus.instance

Вызов метода

Разрешите мне привести пример с использованием шины сессии. Предположим я хочу получить доступ к объекту какого-либо клиента через шину сессии. Этот конкретный D-Bus клиент предоставляет сервис с именем org.gnome.Rhythmbox.

rb_service = bus.service("org.gnome.Rhythmbox")

В этом примере я получаю доступ к сервису org.gnome.Rhythmbox, который в свою очередь предоставлен приложением Rhythmbox.

Отлично, сейчас у меня есть манипулятор (handler) для сервиса и я знаю что он экспортирует объект “/org/gnome/Rhythmbox/Player”. Получить доступ к этому объекту можно с помощью метода object:

rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")

Интроспекция

Отлично, это было просто. Давайте предположим что этот конкретный объект интроспектабелен. Чаще всего так и есть. Объект @rbplayer@, который мы получили, является манипулятором (handler) для удаленного объекта. Обычно такие объекты называютсяпрокси объектами. Было бы хорошо если бы они имели методы, и эти их методы посылали вызовы через D-Bus, которые бы вызывали реальные методы в удаленном объекте (другом процессе). Обнаружить методы дляинтроспектабельных_ объектов достаточно просто:

rb_player.introspect

Обратите внимание, что не все сервисы или объекты интроспектабельны, поэтому вы иногда должны делать это вручную. Позвольте напомнить, что объекты D-Bus имеют интерфейсы а интерфейсы в свою очередь имеют методы. Давайте сейчас получим доступ к этим методам:

rb_player_iface = rb_player["org.gnome.Rhythmbox.Player"]
puts rb_player_iface.getPlayingUri

Вы должны получить правильный интерфейс перед тем как вызвать метод какого-либо объекта. Это немного утомительно, и проще использовать сокращение, которое устанавливает интерфейс для сервиса по умолчанию:

rb_player.default_iface = "org.gnome.Rhythmbox.Player"
puts rb_player.getPlayingUri

Вызов defaultiface=_ указывает интерфейс, который будет использован по умолчанию когда вы напрямую вызываете методы, отсутствующие у прокси-объекта не указывая конкретный интерфейс.

Шина сама по себе имеет соответствующий ей интроспектабельный объект. Вы можете получить к нему доступ через метод bus.proxy. Например вы можете получить массив имен доступных сервисов:

bus.proxy.ListNames[0]

Асинхронный вызов метода

D-Bus асинхронен. Это значит вы не должны ждать до тех пор пока придет ответ на отправленное сообщение. Когда вы вызываете удаленный метод, выполнение которого может занять много времени, вы не хотите чтобы ваше приложение повисло в ожидании ответа. Асинхронность существует для решения этой проблемы. А что если вы не хотите ждать занчание-ответ от удаленного метода но всё-же хотите предпринять какие-нибудь действия когда оно придет?

Есть классический метод программирования таких событийных механизмов. Вы производите какой-нибудь рассчет, вызываете метод и одновременно определяете обработчик, который сработает когда вы получите ответ. В таком случае вы запускаете главный цикл, который ответственнен за правильный запуск обработчиков. Вот как это можно сделать:

rb_player.getPlayingUri do |resp|
  puts "URI трэка #{resp}"
end
puts "Смотри, я не жду ответа!"
loop = DBus::Main.new

loop << bus
loop.run

Этот код напечатает следующее:

Смотри, я не жду ответа!
URI трэка file:///path/to/file.ogg 

(вы ведь жмёте свою музыку в OGG Vorbis, правда?)

Ожидание сигнала

Сигналы - это вызовы посланные вашему приложению из удаленных объектов. Вы конфигурируете приложение на прием и обработку сигнала специальным обратным вызовом (callback). Затем запущенный главный цикл сам настраивается таким образом, чтобы получить сигнал и обработать его обратным вызовом. Вы можете зарегистрировать обработчик сигналов таким образом:

player.on_signal("elapsedChanged") do |event|
  puts event
end

Ещё немного об интроспекции

Есть несколько способов для интроспекции удалённых сервисов. Вы можете просто вызвать метод Introspect() и прочитать XML-вывод. Однако в этом учебнике я предполагаю что вы захотите сделать это с помощью API предоставляемым Ruby D-Bus.

Вы можете получить результат интроспекции от сервиса а не только от объектов.

rb_service = bus.service("org.gnome.Rhythmbox")
rb_service.introspect
p rb_service.root

Эта последовательность выведит древообразную структуру, которая представляет пути к объектам. В нашем случае вывод таков:

</: {org => {gnome => {Rhythmbox => {Player => ..fdbe625de {},
Shell => ..fdbe6852e {},PlaylistManager => ..fdbe4e340 {}}>

Читайте с лева на право: корневой узел это ”/”, он имеет дочерний узел “org”, “org” имеет один дочерний узел “gnome”, и “gnome” имеет один дочерний узел “Rhythmbox”. Rhythmbox имеет дочерние древообразные узлы “Player”, “Shell” и “PlaylistManager”. Эти три последних дочериних узла имеют странные числа, которые говорят нам о том, что они содержат в себе экземпляры объектов. Эти экземпляры объектов уже интроспектированы.

Возможно вам поможет диаграма, если моя проза была не совсем ясна:

  /
    org
      gnome
        Rhythmbox
          Shell (with object)
          Player (with object)
          PlaylistManager (with object)

Обход дерева объектов

Вы можете иметь объект в любом узле, т.е. вы не ограничены верхним уровнем дерева. Вы можете получить доступ к любому узлу:

rb_player = rb_service.root["org"]["gnome"]["Rhythmbox"]["Player"]
rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")

Разница между этими вариантами заключается в том, что для первого случая rb_service должен быть интроспектирован заранее. Так-же в первом случае полученный объект rb_player уже интроспектирован в то время как во втором - нет.

Нет комментариев.

Войдите с помощью OpenID чтобы опубликовать комментарий.
Strio