понедельник, 4 февраля 2013 г.

Repost from: http://laurii.info/2007/03/dbus-and-qt-programming/ For the past few days I’ve been trying to get more complex data sent via DBus. As one would say here in Ireland: 
Je’zus H. Christ
Playing with python was easy. There are lots of examples on communicating complex data structures. But with Qt… It’s a bit more complex, because there are only a set of simple examples.
To keep things simple, I chose the car example:
  • The car/ is the “server”. It receives the request from the client and sends a reply.
  • The controller/ is the “client”. It “calls” a method on the server, gets the results and processes them

<!
DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node name="/com/trollech/examples/car"> <interface name="com.trolltech.Examples.CarInterface"> <method name="accelerate"/> <method name="decelerate"/> <method name="turnLeft"/> <method name="turnRight"/> <method name="structer"> <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MyStruct"/> <arg type="(sxs)" direction="out"/> <arg type="x" direction="out"/> </method> <signal name="crashed"/> </interface> </node>


Notice the extra method called structer with two outputs:
  • a structure MyStruct
  • qlonglong
For the purpose of this test, MyStruct is a simple structure:
class MyStruct {
public:
    QString element1;
    qlonglong element2;
    QString element3;
};
The client calls structer. The server updates the qlonglong and the MyStruct object and sends them back to the client to be used.
h2. The D-Bus translation
First, note the “arg0″ annotation, required for the struct type:
    
This actually helps the qdbusxml2cpp to typ-ify the generated code.
On the client (controller) part, we use the following command:
qdbusxml2cpp -c CarInterface 
             -p car_interface_p.h:car_interface.cpp 
             -i mystruct.h 
             car.xml
On the server (car) part, use the command:
qdbusxml2cpp -i mystruct.h 
             -c CarAdaptor 
             -a car_adaptor_p.h:car_adaptor.cpp 
             car.xml
for the same “car.xml” file.
The XML resulted from the introspection description for structer is translated by the Qt into:
public Q_SLOTS:
    MyStruct structer(qlonglong &out1);
aka first "out" argument is passed as the return argument for the function. The other arguments are passes as function arguments. It's good to know this, because you can implement your own method without looking at the *_p.h files for guidance.
... but not yet.
h2. The Server Side
First issue is that qdbusxml2cpp is not smart enough to implement custom marshaling. It offers instead a guideline for us:
MyStruct CarAdaptor::structer(qlonglong &out1)
{
    // handle method call com.trolltech.Examples.CarInterface.structer
    //return static_cast(parent())->structer(out1);
}
once we change the commented line to something like:
    MyStruct s = static_cast(parent())->structer(out1);

    qDebug() < < "Parameters:" << out1;
    qDebug() << "           " << s.element1 << s.element2 << s.element3;

    return s;
we're good to go. The line
MyStruct s = static_cast(parent())->structer(out1);
implies that the parent() object is a pointer to Car, which, in turn contains a structer()implementation. You can change the call into whatever code capable of updating the out1 parameter and returning a MyStruct struct.
For the purpose of this exercise, we implement a structer() method in our car.cpp like so:
car.cpp:
MyStruct Car::structer(qlonglong &out1) {
    printf("Calling structer (car.cpp)n");
    s.element1 = "one";
    s.element2 = 2;
    s.element3 = "three";
    out1 = 12345;
    return s;
}
and include the "car.h" in.
The downfall of this server approach is that it requires manual adjustment of a generated file. So, once you have the .cpp and the _p.h files available, save them to your repository. Have them regenerated only if needed. Note that trolltech's Qt tarball contains already generated files, instead of scripting :)
h2. The Client Side
Here the fun begins! First, open the .ui file and add a new button to call the structer(). In controller.hcomplete the on_XXXX_clicked() with the new button ID (let's call it on_structer clicked())
The implementation follows the D-Bus guidelines from the other buttons:
void Controller::on_structer_clicked()
{
    qlonglong r2;
    QDBusReply r1 = car->structer(r2);
    printf(">>>>>> %dn", r2);
}
The result is that every time you press the button, it's going to call the server (car) and get the MyStructstructure from the server.
... but not yet! :)
h2. The Glue
D-Bus is a relatively simple mechanism and supports a predefined list of types. Composite types are sent as structures and have to have custom marshaling implemented. This is done via < </>> C++ operators.
Once you have the structure defined, the Qt way is a three-step process.
h3. Declare the Metatype Place the line
Q_CREATE_METATYPE(MyStruct)
somewhere outside body/class/namespace blocks. I've put it in mystruct.h after the struct declaration.
h3. Declare/Implement the Marshaller
This actually means you need to declare/implement the < </>> operators. For convenience, I've declared them in mystruct.h as well and implemented them in mystruct.cpp like this:
h4. mystruct.h
QDBusArgument &operator < < (QDBusArgument &arg,
    const MyStruct &mystruct);
const QDBusArgument &operator >> (const QDBusArgument &arg,
    MyStruct &mystruct);
h4. mystruct.cpp
// Marshall the MyStructure data into a D-BUS argument
QDBusArgument &operator < < (QDBusArgument &arg,
    const MyStruct &mystruct)
{
    arg.beginStructure();
    arg << mystruct.element1 << mystruct.element2 << mystruct.element3;
    arg.endStructure();
    return arg;
}
// Retrieve the MyStructure data from the D-BUS argument
const QDBusArgument &operator >> (const QDBusArgument &arg,
    MyStruct &mystruct)
{
    arg.beginStructure();
    arg >> mystruct.element1 >> mystruct.element2 >> mystruct.element3;
    arg.endStructure();
    return arg;
}
h3. Register the Type with D-Bus
Once you have done the above steps, everything is almost ready. Now you only need to add the type to the list of custom marshalers with qDBusRegisterMetaType.
In mystruct.h, add:
inline void registerCommTypes() {
        qDBusRegisterMetaType();
}
and have it called in both the client and server, somewhere before the actual D-Bus methods are called (e.g. in main() or in some constructor).
Look at the attached example for details.
Good luck at hacking your own D-Bus stuff!

воскресенье, 16 октября 2011 г.

tty0tty

Я привык работать на ноутбуке, но у него нет последовательного порта. Конечно есть переходники usb->serial и т.д. Но когда приходится писать протокол обмена по COM-порту, то очень выручает эта утилита: tty0tty

Суть её сводится к тому, что она эмулирует несколько последовательных, нуль-модемных соединений: 

/dev/tnt0 <=> /dev/tnt1
/dev/tnt2 <=> /dev/tnt3
/dev/tnt4 <=> /dev/tnt5
/dev/tnt6 <=> /dev/tnt7

Напомню распиновку в нуль-модемном соединении:

TX -> RX
RX <- TX
RTS -> CTS
CTS <- RTS
DSR <- DTR
CD <- DTR
DTR -> DSR
DTR -> CD

понедельник, 26 сентября 2011 г.

/* libevent timers example */


#include <stdio.h>

#include <event2/event.h>


struct event_base * base;


struct event * timer1;

struct event * timer2;


struct timeval period1 = {1, 0};

struct timeval period2 = {2, 0};


int tc1 = 10;

int tc2 = 5;


void timer1_handler(int fd, short int fo, void* arg)

{

    printf("timer 1\n");

    if(tc1 > 0)

    {

        tc1--;

        evtimer_add(timer1, &period1);     

    }

}


void timer2_handler(int fd, short int fo, void* arg)

{

    printf("timer 2\n");

    if(tc2 > 0)

    {

        tc2--;

        evtimer_add(timer2, &period2);

    }

}


int main()

{

    struct event_config *cfg = event_config_new();

    event_config_avoid_method(cfg, "epoll");

    base = event_base_new_with_config(cfg);

    event_config_free(cfg);

 

    timer1 = evtimer_new(base, timer1_handler, NULL);

    timer2 = evtimer_new(base, timer2_handler, NULL); 

    evtimer_add(timer1, &period1); 

    evtimer_add(timer2, &period2);

 

    event_base_dispatch(base);

 

    return 0;

}


* This source code was highlighted with Source Code Highlighter.

среда, 8 декабря 2010 г.

Отличный SVN-клиент для Gnome

Искал и нашел - RabbitVCS, неплохо локализован и использует для сравнения мой любимый Meld. Работает шустро и пока серьезных багов не поймал. Теперь еще бы отучиться работать с svn в командной строке:)

вторник, 7 декабря 2010 г.

remserial - работа с последовательным портом через сеть.

Так получилось что одно из устройств, с которым взаимодействует наше оборудование производит другая фирма. Обмен между их устройством и нашим оборудованием происходит по RS232, т.е. по COM-порту. Фирмы наши находятся в противоположных частях города и поэтому стыкуемся мы достаточно редко и не продолжительное время. А вот софт меняется постоянно и нужно отлаживать взаимодействие. Поэтому решили что нужно организовать удаленную отладку, по сети.

На самом деле существует множество способов как получить доступ к COM-порту по сети, но некоторые нам не подошли, а некоторые показались скажем так... больше костылями, чем элегантным и понятным решением. В итоге решили использовать remserial. Схема получилась такая:

наше оборудование <----rs232---->  наш ПК <----internet---> их ПК <---rs232---> их устройство

На обоих ПК стоял Linux, на которых мы собрали remserial (собирается легко, одной командой: make remserial ).

Далее всё просто, я подключил выход, предназначенный для их устройства к COM-порту своего компьютера и запустил в консоли команду:

$ ./remserial -d -p 23000 -s "115200 raw" /dev/ttyS0 &

где 23000 это номер порта, в кавычках опции -s скорость порта и тип (подходят настройки от
stty), а /dev/ttyS0 это имя порта, к которому я подключил наше оборудование.

И всё, дальше я кинул в аську мой внешний ip и номер порта. Ребята на той стороне подключили свое устройство к ПК. И запустили команду:


$ ./remserial -d -r xxx.xxx.xxx.xxx -p 23000 -s "115200 raw" /dev/ttyUSB0 &

Тут отличие от моей команды только в опции -r,  с помощью которой указывают к какому ip адресу подключится ( в нашем случае это xxx.xxx.xxx.xxx ;) ). После чего мы увидели что наши устройства подключились и теперь уже можно было отлаживать взаимодействие без лишних встреч. Вот люблю я  такие программки - мелочь, а сколько времени сэкономила.

Бесплатный NFS сервер для Windows

Понадобилось срочно написать прошивальщик для производства. Сроки горели, а нужно было максимально быстро прошить большое количество модулей для встроенной системы. На производстве человеко-ресурсов мало и требовалось что бы рабочий мог за раз шить десять-двадцать модулей. Модули шьются либо с usb-flash либо по NFS. Выбрал NFS, что бы не тыркаться с флешками. И тут началось...

На производстве стоит Windows, времени переучивать персонал некогда. А вот с NFS сервером проблема, таковых для винды не так много, либо в Cygwin поднимать, либо покупать поделия от малоизвестных контор за денюшку (а еще не известно подойдет мне их NFS сервер или нет). И тут мне фортануло:)

Откопал программку WinNFSd сначала усомнился что она вообще заработает (размер всего 146 Кб, написана в 2005 году). Но как оказалось, работает она отлично. В cmd просто заходите в папку где лежит WinNFSd и запускаете её следующим образом:

> winnfsd.exe c:\shared_folder

где shared_folder - папка, которую вы хотите расшарить по NFS. Что бы получить доступ к этой папке в Linux нужно будет в консоли набрать:

# mount -t nfs -o vers=3 xxx.xxx.xxx.xxx:/c/shared_folder /mnt/nfs 

здесь xxx.xxx.xxx.xxx  - ip адрес компьютера, на котором запущен winnfsd, /mnt/nfs - директория в которую будет смонтирована NFS.

Мои задачи эта программка решила на все сто, но стоит упомянуть, что всё таки это не полноценный NFS сервер по следующим причинам:
  • нет настройки прав доступа
  • возможны проблемы с кодировкой символов
  • может не работать на системах появившихся после Windows XP (на Vista у меня так и не заработала)
В любом случае, это полезная мелочь, которая меня очень, очень выручила, надеюсь пригодится и вам:)