Russian Qt Forum

Qt => Общие вопросы => Тема начата: kuzulis от Август 05, 2009, 17:59



Название: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 17:59
Доброго всем всего!

Вот тут : http://forum.vingrad.ru/forum/topic-268430/kw-qiodevice.html

я пытался узнать, как же формируется сигнал readyRead() при приходе данных в сокет, но мне так никто ничего толком и не рассказал.

Имеется ввиду то, что например когда мы создаем сокет и коннектимся к его сигналу readyRead() - у меня возникает вопрос: а каким образом этот сигнал выдается???

Ведь (если рассматривать к примеру Вин сокеты) для отлова событий от сокета необходимо использовать API функцию select (или WSASelect) . Если посмортеть исходники, то видно, что эту функцию запускает метод класса QAbstractSocket::waitForReadyRead(msecs) - НО ФИШКА В ТОМ, ЧТО мы то не вызываем этот метод! И при этом успешно отлавливаем сигнал readyRead()

Поэтому вопрос: кто/что/какая функция выдает нам сигнал  readyRead() ??? Я запутался в исходниках.. уже не могу понять ничего


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: Авварон от Август 05, 2009, 18:03
ищи emit readyRead() по идее она должна стоять после селекта


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: Alex Custov от Август 05, 2009, 18:35
Поэтому вопрос: кто/что/какая функция выдает нам сигнал  readyRead() ???

По событию на сокете вызывается метод

Цитировать
bool QAbstractSocketPrivate::canReadNotification()
{

    ...

    if (!emittedReadyRead && hasData) {
        emittedReadyRead = true;
        emit q->readyRead();
        emittedReadyRead = false;
    }

    ...

}


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 18:39
select отрабытывет в цикле обработки событий.


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 18:54
Цитировать
По событию на сокете вызывается метод

Цитировать
bool QAbstractSocketPrivate::canReadNotification()
.....

да, это все так.... метод canReadNotification() вызывается например тут:
Код:
bool QAbstractSocket::waitForReadyRead(int msecs)
{
...
         if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
                                               qt_timeout_value(msecs, stopWatch.elapsed()))) {
            d->socketError = d->socketEngine->error();
            setErrorString(d->socketEngine->errorString());
#if defined (QABSTRACTSOCKET_DEBUG)
            qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
                   msecs, d->socketError, errorString().toLatin1().constData());
#endif
            emit error(d->socketError);
            if (d->socketError != SocketTimeoutError)
                close();
            return false;
        }

        if (readyToRead) {
            if (d->canReadNotification())
                return true;
        }

        if (readyToWrite)
            d->canWriteNotification();

        if (state() != ConnectedState)
            return false;
    }
    return false;
}
НО! кто вызывает waitForReadyRead(msecs) ????? :)

Цитировать
select отрабытывет в цикле обработки событий.
где это реализовано в коде? а?  :)


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 18:59
где это реализовано в коде? а?  :)
/src/corelib/kernel/qeventdispatcher_unix.cpp
функция doSelect.


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 19:08
блина, сложно то как!

А если я хочу сделать к примеру аналогично сокету - но для последовательного порта, то что я должен сделать, чтобы при создании класса последовательного порта - в цикле сообщений стало вызываться select (для UNIX) или waitForSingleObject (для Windows) ?

Например в классе последовательного порта реализован метод waitForReadyRead(msecs) c использованием этих ядерных функций : select и waitForSingleObject + к тому класс последовательного порта является наследником от QIODevice - НО я хочу сделать так, чтобы сигнал readyRead() емиттился сам при создании класса! А не только при вызове waitForReadyRead()!


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 19:12
блина, сложно то как!

А если я хочу сделать к примеру аналогично сокету - но для последовательного порта, то что я должен сделать, чтобы при создании класса последовательного порта - в цикле сообщений стало вызываться select (для UNIX) или waitForSingleObject (для Windows) ?

Например в классе последовательного порта реализован метод waitForReadyRead(msecs) c использованием этих ядерных функций : select и waitForSingleObject + к тому класс последовательного порта является наследником от QIODevice - НО я хочу сделать так, чтобы сигнал readyRead() емиттился сам при создании класса! А не только при вызове waitForReadyRead()!
Под linux ничего делать не надо, подсовываешь в QSocketNotifier дескриптор открытого последовательного порта и все будет работать.
С вендой помочь не могу...
Хотя можно попробовать периодически вызывать waitForSingleObject (по таймеру например) и при необходимости генерировать сигнал.
Реализация QSocketNotifier не так уж и сложна. При создании объекта, он регистрируется в глобальном списке. Далее в цикле обработки событий проверяются все зарегистрированные дескрипторы и для тех которые сработали, вызывается сигнал.


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 19:33
Цитировать
Под linux ничего делать не надо, подсовываешь в QSocketNotifier дескриптор открытого последовательного порта и все будет работать.

а поподробнее пжлста! Куда и что подсовывать конкретно? Лучше примерчик!

Цитировать
С вендой помочь не могу...
Хотя можно попробовать периодически вызывать waitForSingleObject (по таймеру например) и при необходимости генерировать сигнал.
ну там не только waitForSingleObject  нужен.. там целая цепочка функций

Цитировать
Реализация QSocketNotifier не так уж и сложна. При создании объекта, он регистрируется в глобальном списке. Далее в цикле обработки событий проверяются все зарегистрированные дескрипторы и для тех которые сработали, вызывается сигнал.
для меня это непонятно :(


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 19:42
а поподробнее пжлста! Куда и что подсовывать конкретно? Лучше примерчик!
Код
C++ (Qt)
int fd = ::open( "/dev/ttyS0", O_RDWR | O_NONBLOCK | O_NOCTTY );
QSocketNotifier *readNotifier = new QSocketNotifier( fd, QSocketNotifier::Read );
connect( readNotifier, SIGNAL( activated( int ) ), ... );
 


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 19:58
Цитировать
....
connect( readNotifier, SIGNAL( activated( int ) ), ... );
....

т.е. далее мне нужно в таком случае написать что-то подобное:
Код:
int fd = ::open( "/dev/ttyS0", O_RDWR | O_NONBLOCK | O_NOCTTY );
QSocketNotifier *readNotifier = new QSocketNotifier( fd, QSocketNotifier::Read );
connect( readNotifier, SIGNAL( activated( int ) , this, SIGNAL(readyRead() );

, где this - это мой класс "последовательный порт" , ккоторый имеет сигнал readyRead() ?

а удалять объект *readNotifier нужно ?


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 20:01
т.е. далее мне нужно в таком случае написать что-то подобное:
Код:
int fd = ::open( "/dev/ttyS0", O_RDWR | O_NONBLOCK | O_NOCTTY );
QSocketNotifier *readNotifier = new QSocketNotifier( fd, QSocketNotifier::Read );
connect( readNotifier, SIGNAL( activated( int ) , this, SIGNAL(readyRead() );
, где this - это мой класс "последовательный порт" , ккоторый имеет сигнал readyRead() ?
Да. И добавить this в конструктор:
Код
C++ (Qt)
QSocketNotifier *readNotifier = new QSocketNotifier( fd, QSocketNotifier::Read, this );


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 05, 2009, 20:09
ааааа!!! спасибо огромное! щас буду пробовать!  Все кажется теперь таким простым... :)

А вот все-таки теперь с виндус проблема остается...  (


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 05, 2009, 20:21
А вот все-таки теперь с виндус проблема остается...  (
Посмотрел я реализацию у тебя в QSerialDevice.
В примитиве, если по таймеру вызывать waitForReadyRead( 0, true ), то получим похожий функционал....
Дальше подумай как это лучше организовать.  ;)


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: Alex Custov от Август 05, 2009, 21:05
НО! кто вызывает waitForReadyRead(msecs) ????? :)

При чём тут вообще waitForReadyRead() ? ;D Ещё раз - по событию на сокете вызывается метод canReadNotification(), который и эмитит сигнал.


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: ритт от Август 06, 2009, 01:21
возможно, поможет
QWinEventNotifier(HANDLE hEvent, QObject *parent = 0)
QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier);


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 06, 2009, 07:57
2 Константин, спасибо за наводку,

я так понимаю, что в качестве дескриптора события  в класс QWinEventNotifier(HANDLE hEvent, QObject *parent = 0), в данном случае можно подставить дескриптор ovl.hEvent (см. код ниже), но! событие о приходе байт в порт формируется не только от ovl.hEvent, но и в ходе анализа
переменной eventMask :
Код:
....
if (eventMask & EV_RXCHAR)
....
(см код ниже)

Код:
....
....
        if(SetCommMask(hd,EV_RXCHAR) == 0) {
            TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->SetCommMask! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
            return retFlag;
        }
        //создаем событие (create event)
        OVERLAPPED ovl;
        ZeroMemory(&ovl, sizeof(ovl));
        ovl.hEvent=CreateEvent(0, false, false, 0);
        if (ovl.hEvent==INVALID_HANDLE_VALUE) {
            TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->CreateEvent! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
            return retFlag;
        }
        //соотносим события от последовательного устройства (!!!???)
        DWORD eventMask=0; //маска событий
        if (WaitCommEvent(hd, &eventMask, &ovl) == 0) {
            if (GetLastError() == ERROR_IO_PENDING){
                switch(WaitForSingleObject(ovl.hEvent, msecs)) {
                    case WAIT_OBJECT_0: retFlag=true; break;
                    case WAIT_TIMEOUT:
                        TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->Timeout! Error!");
if (emitSignals)
if (m_emitSignalErrors) errMsg(EWaitReadyReadTimeout);
                        break;
                    default:
                        TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->Undefined Events! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
                }//switch retVal
            }//if==ERROR_IO_PENDING
            else {
                TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->GetLastError()!=ERROR_IO_PENDING! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
            }
        }//if WaitCommEvent==0
        else {
            if (eventMask & EV_RXCHAR) retFlag=true;
            else {
                TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->(WaitCommEvent!=0) and (eventMask !=EV_RXCHAR)! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
            }
        }//else WaitCommEvent !=0
        SetCommMask(hd,0);
        if (CloseHandle(ovl.hEvent) == 0) {
            TTY_PORTABILITY_DEBUG("TWinSerialDevice::waitForReadyRead->CloseHandle! Error!");
if (m_emitSignalErrors) errMsg(EWaitReadyReadIO);
        }
....
....

тут как-то не пойму как подступиться к этому , но есть мысль: написать в классе "последовательный порт" отдельный метод, используя за базу код, приведеннный выше (с небольшими изменениями) , и вызывать этот новый метод при открытии последовательного порта...

ЗЫ: а каким образом при вставке кода можно сделать подсветку С++ ? какие тэги нужны?


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 06, 2009, 09:56
такс,
1 . просмотрел исходники QWinEventNotifier:
Код:
QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
 : QObject(parent), handleToEvent(hEvent), enabled(false)
{
    QEventDispatcherWin32 *eventDispatcher = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance(thread()));
    Q_ASSERT_X(eventDispatcher, "QWinEventNotifier::QWinEventNotifier()",
               "Cannot create a win event notifier without a QEventDispatcherWin32");
    eventDispatcher->registerEventNotifier(this);
    enabled = true;
}

и я так понял, что специально вызывать QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier) не нужно, т.к. оно уже вызвается в конструкторе

2. Используя QWinEventNotifier - я смогу отловить только факт срабатывания события ! Но узнать что это за событие я не смогу, т.к. для анализа необходимо проверять значения функции Win API - WaitForSingleObject !

3. ИЛИ же проверять переменную eventMask , значение которой возвратила ф-я Win API WaitCommEvent(hd, &eventMask, &ovl)

Такс, теперь бы сделать как-то анализ п.2 и п.3 ... буду думать :)

4. И еще не понятен один момент, как же QSocketNotifier обрабатывает события по типам Read, Write, Exeption ? Ведь в исходниках QSocketNotifier ему в конструктор передается тип (Type type) НО! НИГДЕ ДАЛЬШЕ ОН НЕ ОБРАБАТЫВАЕТСЯ, а просто присваивается приватной переменной sntype ....

В общем вопросов больше чем ответов :(


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 06, 2009, 10:10
2. Используя QWinEventNotifier - я смогу отловить только факт срабатывания события ! Но узнать что это за событие я не смогу, т.к. для анализа необходимо проверять значения функции Win API - WaitForSingleObject !
В конструкторе QWinEventNotifier ты передаешь хендл своего события, которое будет ждать этот нотификатор (будет вызываться WaitForSingleObject для этого события) и как только это событие произойдет, будет послан сигнал activated( HANDLE ) с хендлом этого события.
Т.е. тебе нужно создать и настроить событие, а Qt будет проверять его и сообщит тебе, когда оно произошло.


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 06, 2009, 10:17
Цитировать
В конструкторе QWinEventNotifier ты передаешь хендл своего события, которое будет ждать этот нотификатор (будет вызываться WaitForSingleObject для этого события) и как только это событие произойдет, будет послан сигнал activated( HANDLE ) с хендлом этого события.
Т.е. тебе нужно создать и настроить событие, а Qt будет проверять его и сообщит тебе, когда оно произошло.
дык тут WaitForSingleObject  возвратить может или WAIT_OBJECT_0 или WAIT_TIMEOUT , а нужное мне событие - это WAIT_OBJECT_0 ! Но я не узнаю что это.. таймаут или пришли данные! :)

упс сорри, глянул в исходники, и там идет проверка на WAIT_OBJECT_0

Код:
void QEventDispatcherWin32::activateEventNotifiers()
{
    Q_D(QEventDispatcherWin32);
    //### this could break if events are removed/added in the activation
    for (int i=0; i<d->winEventNotifierList.count(); i++) {
        if (WaitForSingleObjectEx(d->winEventNotifierList.at(i)->handle(), 0, TRUE) == WAIT_OBJECT_0)
            d->activateEventNotifier(d->winEventNotifierList.at(i));
    }
}

Тут в принципе теперь все вроде понятно, но остается вопрос с отловом события через :
Код:
        if (WaitCommEvent(hd, &eventMask, &ovl) == 0) {
...
...
        }//if WaitCommEvent==0
        else {
            if (eventMask & EV_RXCHAR) retFlag=true;


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 06, 2009, 10:20
дык тут WaitForSingleObject  возвратить может или WAIT_OBJECT_0 или WAIT_TIMEOUT , а нужное мне событие - это WAIT_OBJECT_0 , поэтому QWinEventNotifier при приходе данных в порт ИЛИ при таимауте возвратит это событие! Но я не узнаю что это.. таймаут или все пучком! :)
WaitForSingleObject вызывается без блокировки (ожидания), т.е. тайм-аута быть не может.
Поэтому он вызывается в цикле обработки событий, т.е. постоянно. Вызвали - события нет, пошли дальше, вызвали - событие произошло, сообщили об этом, пошли дальше....


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 06, 2009, 10:32
Тут в принципе теперь все вроде понятно, но остается вопрос с отловом события через :
Код:
        if (WaitCommEvent(hd, &eventMask, &ovl) == 0) {
...
...
        }//if WaitCommEvent==0
        else {
            if (eventMask & EV_RXCHAR) retFlag=true;
А может быть или так или так? Забавно.  ;)


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 06, 2009, 10:43
Цитировать
А может быть или так или так? Забавно.  Подмигивающий
аха! :)

http://www.vsokovikov.narod.ru/New_MSDN_API/Comm_res/fn_waitcommevent.htm


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: BRE от Август 06, 2009, 11:11
Цитировать
А может быть или так или так? Забавно.  Подмигивающий
аха! :)

http://www.vsokovikov.narod.ru/New_MSDN_API/Comm_res/fn_waitcommevent.htm
Любопытно, т.е. основную проверку делает WaitCommEvent, а если операция не может завершиться немедленно, то в GetLastError() - ERROR_IO_PENDING и ожидаем по WaitForSingleObject.
Хмм. Как все не просто.
Тогда нужно постоянно вызывать WaitCommEvent для проверки....

[off]Почитал доку по нескольким функциям и нахлынуло, как же Микрософт любит все усложнить...  ;)[/off]


Название: Re: Принцип формирования сигналов на примере QTcpSocket
Отправлено: kuzulis от Август 07, 2009, 14:39
такс, немного поразбирался с текущей реализацией в QT событий, и решил создавать СВОИ события при работе с последовательным портом !

просмотрел класс QEvent и однаружил там это: QEvent::User, т.е. я хочу например создать свое(юзер-определяемое) событие при работе порта, аналогичное QEvent::SockAct , т.к. QSocketNotifier не отвечает поставленным требованиям в полной мере, т.е. работает только для POSIX ОС!

Я хочу сделать что то типа QSerialNotifier который бы охватывал и POSIX и WIN32 ОС!

Очень уж хочется создать класс для работы с последовательным устройством по всем принципам QT (т.е. аналогично QAbstractSocket и т.п.) !!!

Помогите пожалуйста знатоки определится с чего начать :)

Исходные данные:
1. Для POSIX для отлова событий от порта используется ::select (тут в принципе QSocketNotifier  годится)
2. Для Виндовс для отлова событий от порта необходимо использовать WaitCommEvent в связке с WaitForSingleObject. (тут можно пошаманить с QWinEventNotifier )

Хочу:
1. Создать какой-то общий класс, аналогичный QSocketNotifier  и QWinEventNotifier который бы отражал всю глубину глубин (я сам не знаю что)

В принципе я смог бы (для тестирования хотябы в линуксе) использовать  QSocketNotifier   - но тогда возникает вопрос : КАК ПРАВИЛЛЬНО РЕАЛИЗОВАТЬ тогда метод waitForReadyRead(msecs) и waitForBytesWritten(msecs) c учетом того, что эти методы должны зависят и от QSocketNotifier   !!!!

т.е. короче аналогично тому  как в QAbstractSocket сделано - чтобы и события "правильно" в петле обрабатывалить и "правильно" работали методы waitForReadyRead и т.п.

ЗЫ: извиняюсь за сумбур.. я еще не представляю четко то что нужно..