Russian Qt Forum
Ноябрь 26, 2024, 10:22 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Обработка сигналов в QT  (Прочитано 15405 раз)
andi
Гость
« : Март 27, 2006, 13:03 »

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

Под Qt есть либа - qextserialport, - топик где-то тут проскакивал. А вообще, проще всего открыть дескриптор ком-порта, в обычном select-е дожидаться появления данных и также читать-писать... 200-300 строк кода... И завернуть затем в сигналы qt, если нужно.
Записан
andi
Гость
« Ответ #2 : Март 27, 2006, 16:50 »

Указанную библу качнул, еще пристально не разглядывал, но поверхностный осмотр (а также чтение того самого топика)... говорят, она не поддерживает асинхронный обмен (который мне как раз и нужен).
А про select можно поподробней.
Для tcp-сокетов я вроде как его использовал (давно было, не помню).
А вот для файловых дескрипторов чего-то даже непонятно как прикрутить.
Если есть кусок рабочего примера, буду признателен.
Кстати, какой класс используется для обработки сигналов системы? Можно ли из Qt на них вешать свои обработчики?
Записан
Hordi
Гость
« Ответ #3 : Март 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;
}
Записан
alex0303
Гость
« Ответ #4 : Март 27, 2006, 21:20 »

Под linux-ом для работы с любым железом драйвер которого умеет работать с асинхронным вводом выводом замечательно подходит QSocketNotifier

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

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

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

В результате асинхронность без опроса по таймеру/бездействию и без дополнительных потоков. (хотя второй(ые) поток(и) qt иногда сама генерит).
Записан
andi
Гость
« Ответ #5 : Март 28, 2006, 12:24 »

Спасибо всем кто откликнулся.
Сегодня буду экспериментировать.
Записан
andi
Гость
« Ответ #6 : Март 29, 2006, 08:10 »

Почитал про QSocketNotifiler... не понятно как таймаут обрабатывать.
Я так понял, надо вешать внешний таймер. Запускать его при отправке посылки, и если ответ пришел до срабатывания таймера, радоваться ответу?

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

Все работает.
Записан
alex0303
Гость
« Ответ #7 : Март 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 интерфейса.
Записан
andi
Гость
« Ответ #8 : Март 31, 2006, 14:01 »

Как в последнем примере узнать, что устройство молчит уже 2 минуты?
Записан
alex0303
Гость
« Ответ #9 : Март 31, 2006, 19:09 »

Цитата: "andi"
Как в последнем примере узнать, что устройство молчит уже 2 минуты?


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

Втречный вопрос: а как вы с select() ждёте ответа в течении 2-х минут?
 - С "заморозкой" интерфейса (в случае отсутствия данных на 2 мин.)?
 - Частым опросом с минимальным/нулевым временем ожиданий?
 - В отдельном потоке?
Записан
andi
Гость
« Ответ #10 : Апрель 05, 2006, 05:08 »

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

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


Если это GUI прога, то ИМХО токой подход непримлим.
Записан
Hordi
Гость
« Ответ #12 : Апрель 05, 2006, 11:36 »

Я в подобных случаях делаю так:

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

Индикатор можно сделать через модальный диалог - тогда все проще
Записан
agrigoriev
Гость
« Ответ #13 : Апрель 05, 2006, 15:11 »

Я переделывал эту либу для non-block чтения, причем не только под linux но и под win. Если нужно, могу прислать...
Записан
ElderOrb
Гость
« Ответ #14 : Апрель 05, 2006, 21:14 »

Цитата: "agrigoriev"
Я переделывал эту либу для non-block чтения, причем не только под linux но и под win. Если нужно, могу прислать...


Я тоже переделывал, а точнее наследовался от QIODevice и по образу и подобую делал свой QSerial под win32 с использованием отложенных чтения/записи. Но, во-первых, сделал только то что нужно было только на тот момент, во-вторых, не кроссплатформенно, а в третьих очень интересно посмотреть на альтернативное решение. Так что если не трудно, вышли на ai@kbtem.by Улыбающийся
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.179 секунд. Запросов: 21.