Russian Qt Forum

Qt => Дополнительные компоненты => Тема начата: andi от Март 27, 2006, 13:03



Название: Обработка сигналов в QT
Отправлено: andi от Март 27, 2006, 13:03
Здрасте всем.
Пишу под Linux на Qt 3.x.x (то что идет со Slackware 10)
Переношу на Qt приложение которое общается с внешним миром через serial port. В консоле необходимая интерактивность осуществлялась при помощи перехвата SIGIO обработчиком, который устанавливал флаг.
Флаг время от времени проверялся и если был установлен производилось  чтение из порта (порт открывался при помощи open(), флаги чтени-запись и неблокируемое чтение).
Аналогичная фича под Qt не пошла.
Весьма вероятно что есть другое решение (свойственное Qt).
Задачу бы я сформулировал так:
Нужно настроить файловый дескриптор таким образом чтобы можно производить неблокирующее чтение. И можно было узнавать момент поступления данных в порт.


Название: Обработка сигналов в QT
Отправлено: Hordi от Март 27, 2006, 13:36
Под Qt есть либа - qextserialport, - топик где-то тут проскакивал. А вообще, проще всего открыть дескриптор ком-порта, в обычном select-е дожидаться появления данных и также читать-писать... 200-300 строк кода... И завернуть затем в сигналы qt, если нужно.


Название: Обработка сигналов в QT
Отправлено: andi от Март 27, 2006, 16:50
Указанную библу качнул, еще пристально не разглядывал, но поверхностный осмотр (а также чтение того самого топика)... говорят, она не поддерживает асинхронный обмен (который мне как раз и нужен).
А про select можно поподробней.
Для tcp-сокетов я вроде как его использовал (давно было, не помню).
А вот для файловых дескрипторов чего-то даже непонятно как прикрутить.
Если есть кусок рабочего примера, буду признателен.
Кстати, какой класс используется для обработки сигналов системы? Можно ли из Qt на них вешать свои обработчики?


Название: Обработка сигналов в QT
Отправлено: Hordi от Март 27, 2006, 18:18
Асинхронный обмен эта либа не поддерживает...

Использование select, read, write ничем не отличается от работы с сокетами, все как в man так и есть. Единственное отличие - это нужно порт перед использованием настраивать (битность там всякая, скорость передачи и т.п.)

Если под сигналами ты монимаешь именно SIGIO и т.п., то для чтения данных этот сигнал использовать неправильно, потому как:
1.На функцию-обработчик (непосредственную, которой ловим сигнал) сигнала есть масса ограничений, типа в ней можно вызывать ТОЛЬКО определенные функции, нельзя устанваливать переменные и т.п. Короче man signal
2.SIGIO срабатывает ОЧЕНЬ часто, при ЗАПИСИ в порт и при ЧТЕНИИ. Использовать сигнал для ожидания данных - не хорошо,select намного удобнее и проще.
3.Сигнал может не дойти, дойти с опозданием и т.п.

Пример установки параметров:
Код:

bool CComPort::open( const char *port, ePortSpeed speed, int params )
{
if( !port || isOpened() ) return false;

m_parms = 0;

m_pd = ::open(port, O_RDWR|O_NDELAY|O_NOCTTY);
if(m_pd < 0)return false;


if(lockf(m_pd, F_TLOCK, 0) == -1){ // already locked
::close(m_pd);
m_pd = -1;
return false; // return error
}

long flags = 0;
fcntl(m_pd, F_GETFL, flags);

if(m_type & PT_Async) flags |= O_ASYNC;
if(m_type & PT_NonBlock) flags |= O_NONBLOCK;

fcntl(m_pd, F_SETFL, flags);

changeParms( speed, params );
m_parms = params;
return true;
}

void CComPort::changeParms( ePortSpeed speed, int params )
{
if( !isOpened() ) return;
tcgetattr(m_pd, &m_oldtio);

m_newtio = m_oldtio;

m_newtio.c_cflag|=CREAD|CLOCAL;

m_newtio.c_lflag&=~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG);
m_newtio.c_iflag&=~(INLCR|IGNCR|ICRNL|INPCK|IGNPAR|PARMRK|ISTRIP|IXANY);
m_newtio.c_oflag&=~OPOST;
m_newtio.c_cc[VMIN]=1;
m_newtio.c_cc[VINTR] = _POSIX_VDISABLE;
m_newtio.c_cc[VQUIT] = _POSIX_VDISABLE;
m_newtio.c_cc[VSTART] = _POSIX_VDISABLE;
m_newtio.c_cc[VSTOP] = _POSIX_VDISABLE;
m_newtio.c_cc[VSUSP] = _POSIX_VDISABLE;

cfsetispeed(&m_newtio, speed);
cfsetospeed(&m_newtio, speed);

m_newtio.c_cflag = (params & PP_StopBits) ? m_newtio.c_cflag | CSTOPB
: m_newtio.c_cflag & (~CSTOPB);

m_newtio.c_cflag = (params & PP_Parity) ? m_newtio.c_cflag | PARENB
      : m_newtio.c_cflag & (~PARENB);

m_newtio.c_cflag = (params & PP_ParityOdd) ? m_newtio.c_cflag | PARODD
         : m_newtio.c_cflag & (~PARODD);

m_newtio.c_cflag = (params & (PP_7bit | PP_8bit )) ? m_newtio.c_cflag & (~CSIZE)
: m_newtio.c_cflag | CSIZE;
if( params & PP_8bit )      m_newtio.c_cflag |= CS8;
else if( params & PP_7bit ) m_newtio.c_cflag |= CS7;

//flow_hardware
//m_newtio.c_cflag|=CRTSCTS;
m_newtio.c_iflag&=(~(IXON|IXOFF|IXANY));

tcflush(m_pd, TCIFLUSH);
tcflush(m_pd, TCOFLUSH);

tcsetattr(m_pd, TCSANOW, &m_newtio);

m_parms = params;
}


Название: Обработка сигналов в QT
Отправлено: alex0303 от Март 27, 2006, 21:20
Под linux-ом для работы с любым железом драйвер которого умеет работать с асинхронным вводом выводом замечательно подходит QSocketNotifier

Т.е. Вы передаёте файловый дескриптор в один или несколько (отдельно для read/write/error) конструкторов QSocketNotifier, а он его засовывает в pool/select стандартного цикла обработки сообщений QT.

Ну а далее ловите сигналы  activated() от этих сокетов и соответственно обрабатываете.

Проверял на себе.

В результате асинхронность без опроса по таймеру/бездействию и без дополнительных потоков. (хотя второй(ые) поток(и) qt иногда сама генерит).


Название: Обработка сигналов в QT
Отправлено: andi от Март 28, 2006, 12:24
Спасибо всем кто откликнулся.
Сегодня буду экспериментировать.


Название: Обработка сигналов в QT
Отправлено: andi от Март 29, 2006, 08:10
Почитал про QSocketNotifiler... не понятно как таймаут обрабатывать.
Я так понял, надо вешать внешний таймер. Запускать его при отправке посылки, и если ответ пришел до срабатывания таймера, радоваться ответу?

Решил сделать через select (Стивенс как обычно рулит, есть готовые решения и примеры. Там же прочитал, что select можно использовать для любых дескрипторов).

Все работает.


Название: Обработка сигналов в QT
Отправлено: alex0303 от Март 29, 2006, 10:52
Цитата: "andi"
Почитал про QSocketNotifiler... не понятно как таймаут обрабатывать.
Я так понял, надо вешать внешний таймер. Запускать его при отправке посылки, и если ответ пришел до срабатывания таймера, радоваться ответу?


Про таймер не очень понял. Если Вам надо ждать ответ в течении некоторого времени то это уже более высокий уровень.
Я говорил про примерно следующее:
Код:

CMySerialObject::open(......)
{
    // Открываем файл и т.д.
    ...
    // пусть fd открытый файловый дескриптор, и настроенный на асинхронную передачу

    QSocketNotifier* pSNRead = new QSocketNotifier (fd, QSocketNotifier::Read, this);
    QSocketNotifier* pSNWrite = new QSocketNotifier (fd, QSocketNotifier::Write, this);
    QSocketNotifier* pSNError = new QSocketNotifier (fd, QSocketNofifier::Exception, this);

    connect(pSNRead, SIGNAL(activated(int)), this, SLOT(slotReadyRead(int)));
    connect(pSNWrite, SIGNAL(activated(int)), this, SLOT(slotReadyWrite(int)));
    connect(pSNError, SIGNAL(activated(int)), this, SLOT(slotError(int)));
}

CMySerialObject::slotReadyRead(int fd)
{
    // Читаем новые данные, желательно вс а то опять сработает :)
    ...

}

CMySerialObject::slotReadyWrite(int fd)
{
    // Можно посылать новые данные
    ...

}

CMySerialObject::slotError(int fd)
{
    // Ошибочка вышла :)
    ...

}


Можно сказать что тот-же select() с бесконечным таймаутом, всё в одном потоке и без тормозов GUI интерфейса.


Название: Обработка сигналов в QT
Отправлено: andi от Март 31, 2006, 14:01
Как в последнем примере узнать, что устройство молчит уже 2 минуты?


Название: Обработка сигналов в QT
Отправлено: alex0303 от Март 31, 2006, 19:09
Цитата: "andi"
Как в последнем примере узнать, что устройство молчит уже 2 минуты?


Добавить таймер :)
Но это уже реализация Вашего протокола, т.е. более высокий уровень.

Втречный вопрос: а как вы с select() ждёте ответа в течении 2-х минут?
 - С "заморозкой" интерфейса (в случае отсутствия данных на 2 мин.)?
 - Частым опросом с минимальным/нулевым временем ожиданий?
 - В отдельном потоке?


Название: Обработка сигналов в QT
Отправлено: andi от Апрель 05, 2006, 05:08
Использую "заморозку" на 2 мин. В моей задаче это не критично, пока данные не придут ей больше делать нечего. Ну и ждать вечно смысла нет, 2 мин как раз.


Название: Обработка сигналов в QT
Отправлено: alex0303 от Апрель 05, 2006, 08:46
Цитата: "andi"
Использую "заморозку" на 2 мин. В моей задаче это не критично, пока данные не придут ей больше делать нечего. Ну и ждать вечно смысла нет, 2 мин как раз.


Если это GUI прога, то ИМХО токой подход непримлим.


Название: Обработка сигналов в QT
Отправлено: Hordi от Апрель 05, 2006, 11:36
Я в подобных случаях делаю так:

блокирующая функция А (работа с девайсами например) запускается в отдельном потоке, в главной программе показываю индикатор процесса (show) и в циклюсь (в этом цикле вызываю qApp->processEvent() и проверяю результат моей A-функции, как только результат пришел закрываю индикатор и показываю результат. Перед show индикатора блокируюся все нажатия клавишь и обработчики.

Индикатор можно сделать через модальный диалог - тогда все проще


Название: Обработка сигналов в QT
Отправлено: agrigoriev от Апрель 05, 2006, 15:11
Я переделывал эту либу для non-block чтения, причем не только под linux но и под win. Если нужно, могу прислать...


Название: Обработка сигналов в QT
Отправлено: ElderOrb от Апрель 05, 2006, 21:14
Цитата: "agrigoriev"
Я переделывал эту либу для non-block чтения, причем не только под linux но и под win. Если нужно, могу прислать...


Я тоже переделывал, а точнее наследовался от QIODevice и по образу и подобую делал свой QSerial под win32 с использованием отложенных чтения/записи. Но, во-первых, сделал только то что нужно было только на тот момент, во-вторых, не кроссплатформенно, а в третьих очень интересно посмотреть на альтернативное решение. Так что если не трудно, вышли на ai@kbtem.by :)


Название: Обработка сигналов в QT
Отправлено: andi от Апрель 06, 2006, 08:49
Я бы тоже неотказался посмотреть на библиотеку. Ну и пример использования потоков в ГУИ.
andi123(собака)yandex.ru