pondělí 9. září 2013

Volání metody DBus služby - Shell, Python, Vala

Je tu další příspěvek pro programovací menšinu. Cvičně si přepisuju ty mé nedodělky do Vala, abych je mohl mít nedodělané o něco rychlejší a úspornější. Dokumentace ke GTK a Vala už mám v prohlížeči otevřeno asi 80 tabů a jsem rád, že Opera v nich umí vyhledávat podle názvu a url. Mimochodem, umí vůbec něco takového konkurence? Každopádně jsem se dopracoval k základnímu pochopení přístupu Vala k DBus, což mému zeskriptovatělému mozku chvíli trvalo a tak vám pro porovnání ukážu, jak volat metody nějaké běžící DBus služby z shellu, Pythonu, i Vala. ...


D-Bus

Co je D-Bus jsem tu už prezentoval dříve, je to systémová sběrnice, po které spolu mohou komunikovat různé aplikace. V XML formátu. DBus běží ve dvou vrstvách, systémové a uživatelské pro každé sezení (session). Aplikace může fungovat jako server, klient, nebo obojí. Server nabízí svůj interface jiným, client ho pak může využívat. Interface může obsahovat metody a signály. Jako klient se můžete připojit na signály jiné aplikace a na ně navázat volání metod aplikace vlastní. Například aplikace email vyšle signál, že přišla zpráva a všechny aplikace, které se na tento signál připojily, na to mohou reagovat. Třeba mohou zavolat DBus metodu aplikace email, která jim vrátí odesílatele a znění subjektu a zobrazit je v systémové notifikaci, samozřejmě také přes DBus. Ale dnes signály řešit nebudu, jen si do nějaké služby cvičně jednorázově šťouchnu.

D-Feet D-Bus debuger

Na prolézání a testování DBus sběrnice je D-Feet slušný nástroj. Umí vyhledávat, ke každé metodě vám napíše jaké chce parametry a co vrací a můžete metody DBus rovnou testovat. V Ubuntu 12.04 mi funguje okno aplikace normálně, ale v 13.04 je jakési rozmrdané. Když vám začne kvůli delším textům lézt obsah mimo okno doleva(!), i s posuvníkem, nezbyde, než celé okno zvětšit, nebo měnit velikost postranního panelu. Jinak je to psané v Pythonu a GTK.

Instalace

sudo apt-get install d-feet



Každopádně si zde můžete najít služby, o které stojíte a zjistit si potřebná data, jako dostupné interfejsy, metody včetně parametrů a signály. Pokud je XML kód služby řádně popisuje, mělo by být krom typu parametru jasné i to, k čemu je dobrý. Dvojklik na konkrétní metodu vyvolá dialog, který umožní metodu zavolat s potřebnými argumenty, které sem napíšete a také zobrazit výstup.

Jak se do DBus volá, tak se z něho ozývá

Nebudu tu propírat kompletní obsluhu DBus, ukážu jen to, jak je možné volat metody existující služby ve zmíněných jazycích. Pro příklad jsem zvolil screensaver GNOME. Na obrázku z D-Feet můžete vidět strukturu této služby, která krom spouštění spořiče zamyká obrazovku, následně na ní umí zobrazit zprávu pro uživatele a také podává zprávy o změně a stavu spořiče. Dokonce umí simulovat aktivitu uživatele, takže pokud chcete zabránit jeho aktivaci, můžete v intervalu volat příslušnou metodu. No a jak takové volání DBus metod vypadá...

Shell

#!/bin/sh

dbus-send --type=method_call --dest=org.gnome.ScreenSaver / \
org.gnome.ScreenSaver.Lock

dbus-send --type=method_call --dest=org.gnome.ScreenSaver / \
org.gnome.ScreenSaver.ShowMessage \
string:"Titulek" string:"Tělo zprávy" string:"ikona"

Řešení přímočaré, prostě napíšete jméno služby, cestu k němu (v tomto případě jsem napsal pouze lomítko, protože si to dbus-send umí najít sám, když tam není víc možností), jméno interfejsu a přímo za něj přes tečku metodu a její parametry. Jsou to příkazy jednořádkové, zpětná lomítka ho umožňují rozdělit na řádků více, aby to bylo přehlednější. Pokud tento skript spustíte, zamkne se obrazovka a pak se na ní zobrazí zpráva pro uživatele. Ikonu to normálně nezobrazuje, i když dáte správnou cestu, prázdný řetězec udělá to samé, to platí obecně.

Upozorňuji, že třeba v Ubuntu 12.04 chce ještě metoda Lock parametr typu string (stačí prázdný), D-Feet vám to ukáže.

Python

V Pythonu si vyžádáte přes modul dbus proxy dané služby, proxy požádáte o konkrétní metodu a tu pak voláte. Dá se to naprasit i na jeden řádek..

#!/usr/bin/env python3
import dbus

session_bus  = dbus.SessionBus ()
proxy        = session_bus.get_object (
                "org.gnome.ScreenSaver",
                "/org/gnome/ScreenSaver")

set_active   = proxy.get_dbus_method ("SetActive",  "org.gnome.ScreenSaver")
lock         = proxy.get_dbus_method ("Lock",       "org.gnome.ScreenSaver")
show_message = proxy.get_dbus_method ("ShowMessage","org.gnome.ScreenSaver")

set_active (True)
lock ()
show_message ("Titulek", "Tělo zprávy", "ikona")


Vala

Vala vyžaduje trochu jiný přístup. Když si od DBus knihovny vyžádáte proxy nějaké služby, dostanete objekt, který má jakýsi obecný typ a je třeba mu dopsat inteface, jenž definuje jeho metody a signály v rozsahu, který potřebujete. Interface přiřadíte onomu proxy objektu, čímž tento přestane být pro kompilátor neznámým a metody dané interfejsem můžete volat běžným způsobem.

[DBus (name = "org.gnome.ScreenSaver")]
interface ScreenSaver : Object
{
    public abstract void set_active (bool active) throws IOError;
    public abstract void lock () throws IOError;
    public abstract void show_message (string summary,
                                       string body,
                                       string icon) throws IOError;
}

public int main (string[] args)
{
    try {
        ScreenSaver proxy = Bus.get_proxy_sync
                ( BusType.SESSION,
                  "org.gnome.ScreenSaver",
                  "/org/gnome/ScreenSaver" );

        proxy.set_active (true);
        proxy.lock ();
        proxy.show_message ( "Titulek",
                             "Tělo zprávy",
                             "ikona");
    }
    catch (Error e) {
        print ("Něco se podělalo!\n");
        return 1;}
    return 0;
}


Kompilace:
valac --pkg gio-2.0 soubor.vala

Jak je vidět, interface je třeba uvést speciálním zaklínadlem (atributem) se jménem DBus interfejsu, který má popisovat. V něm si deklarujete metody, které budete chtít v rámci služby používat. Metody by měly mít dovětek throws IOError. Vala také předpokládá, že názvy metod a signálů DBusu přepíšete do interfejsu z DBusCamelCaseNames na vala_lower_case_names. ShowMessage tedy bude show_message a použitá knihovna si to přežvýká na konkrétní DBus jméno. Ovšem i pokud použijete původní názvy DBus metod, bude váš program volat správné metody, jen budou v rámci konvencí Vala vypadat jako třídy, místo metod. Vizuálně v kódu. Další zajímavostí je fakt, že pokud jsem schválně umazal konec cesty k objektu při získávání proxy (to je ten druhý parametr s lomítky, popravdě jsem vymazal skoro vše a nechal jen první tři písmena za org/), program fungoval bez problémů, knihovník si cestu k interfejsu prostě domyslel, protože tam nic jiného nebylo. Metody služeb na DBus ovšem nemusí dodržovat naznačené konvence a na rozdíl od Vala rozliší jména ShowMessage a show_message ... Ale asi nikdo takové kraviny dělat nebude... ;)

Obsluhu výjimky vám připomene překladač, pokud ji zapomenete, protože ví, že v práci s DBus může nastat IOError, každopádně může nastat i jiný error, když se třeba změní jméno interfejsu, o změně jména sužby nemluvě. Obecným Errorem zachytíte i tyto chyby, nejen ty komunikační, napsal jsem ho tam, abych neměl počmáranou celou obrazovku, když jsem testoval co všechno jsou si Vala/DBus schopny domyslet.

Závěr

Zkrátka různý přístup k jednomu problému. Jsem teprve na začátku a zatím mi není jasné, jak bych ve Vala dosáhl volání metody, kterou bych si za běhu na DBus zjistil přes introspekci. Například, kdybych chtěl přepsat D-Feet do Vala. Snad časem..

Faktem je, že přístup Vala k DBus může programátorovi přidat trochu psaní navíc. Ve chvíli, kdy potřebujete otestovat více služeb a vybrat si podle dostupnosti tu, kterou skutečně použijete, budete mít třeba v Pythonu psaní o poznání méně a to i vzhledem k ne/typovosti použitého programovacího jazyka. Na to, jak může vypadat třída, která má na starosti selekci a následné použití dostupné služby, se můžete podívat zde. Tím, že kompilátor dopředu potřebuje znát vlastnosti volaného objektu, se věci malinko komplikují.


2 komentáře:

  1. Moc pěkný porovnání, i když tíhnu k tomu skriptovacímu pojetí, tak kód ve Vale je taky hezkej ;-)

    OdpovědětSmazat
    Odpovědi
    1. Je to jednoduché a přehledné. Jen jsem trochu v popisu pomotal co je co a co je důležité, už jsem to opravil. Já každopádně obecně tíhnu k Vala, protože ten kód je pro mě přehlednější. Všude přesně vidím, jaké parametry kam lezou (nutnost definovat typ na každém vstupu) a díky tomu taky začínám lépe chápat fungování knihoven, které používám, například GTK, ke které mám přesnou dokumentaci, protože céčková sedí. Plno věcí je překvapivě s Vala jednodušších, než s Pythonem, začíná to tím, že když si třeba pro přehlednost program rozhážu do x souborů, nemusím řešit žádné moduly, importy, kraviny, jen kompilátoru vyjmenuju ty soubory a hotovo. Bastlí se mi v tom fakt líp :)

      Smazat

Zkuste prosím při komentováni používat místo volby Anonymní volbu Název/adresa URL, kde vyplníte nějakou přezdívku, adresu zadávat nemusíte. Vědět, které příspěvky jsou od jednoho člověka, je fajn. Díky.

Pokud by se vám náhodou odeslaný komentář na stránce nezobrazil, vytáhnu ho z koše hned jak si toho všimnu. I Google spam filter se občas sekne.