Russian Qt Forum

Qt => Вопросы новичков => Тема начата: titan83 от Ноябрь 11, 2013, 16:49



Название: QtSerialPort и определение конца пакета (решено)
Отправлено: titan83 от Ноябрь 11, 2013, 16:49
Всем доброго времени суток.
Возник вопрос такого порядка - при использовании QtSerialPort с определенной периодичностью (в среднем раз 15-20 кадров) происходит разрыв принятого кадра на два меньших. Например, было 01-05-00-00-00-20-FF-FF, а стало 01-05-00-00 и следующий кадр 00-20-FF-FF, потом все принимается нормально. Аналогично происходит на текстовых сообщениях. Пробовал играть с размером буфера приема, но как-то улучшить ситуацию это не помогло. Можно ли как-то контролировать время, после которого QtSerialPort считает кадр завершенным?


Название: Re: QtSerialPort и определение конца пакета
Отправлено: kuzulis от Ноябрь 11, 2013, 20:51
Цитировать
Можно ли как-то контролировать время, после которого QtSerialPort считает кадр завершенным?

Только с помощью дополнительного QTimer.

Или использовать handle() метод (в Qt 5.2) и устанавливать платформо-специфичные таймауты самостоятельно через SetCommTimeouts(), к примеру, но это будет криво.

Цитировать
Пробовал играть с размером буфера приема, но как-то улучшить ситуацию это не помогло.

Буфер тут вообще ни при чем...

ЗЫ: В будущем возможно добавится некоторый АПИ для обработки таймаутов, но пока непонятно как его реализовать корректно и т.п.
 


Название: Re: QtSerialPort и определение конца пакета
Отправлено: titan83 от Ноябрь 12, 2013, 06:48
Т.е. по сигналу readyRead() я запускаю таймер, например, на 20 мс (у меня меньше 15 мс система не отрабатывает), а уже на его сигнал timeout() вешаю readAll() и всю свою логику?


Название: Re: QtSerialPort и определение конца пакета
Отправлено: alex312 от Ноябрь 12, 2013, 10:35
titan83, тебе надо использовать какой нибудь протокол обмена. Потому что как ты не контролируй время выборки из порта, ты всеравно не сможешь подстроится под реальный мир, и будут ситуации когда между опросами прийдет 0,5 пакета, или 1,23. Короче просто невозможно синхронизировать приемник и передатчик по времени.


Название: Re: QtSerialPort и определение конца пакета
Отправлено: kuzulis от Ноябрь 12, 2013, 10:46
Цитировать
Т.е. по сигналу readyRead() я запускаю таймер, например, на 20 мс (у меня меньше 15 мс система не отрабатывает), а уже на его сигнал timeout() вешаю readAll() и всю свою логику?

Да как-то так:
Код
C++ (Qt)
 
YourClass::handleReadyRead()
{
   if (port->bytesAvailable() < expected) {
       if (!timer->isActive())
           timer->singleShot(20);
   } else {
       timer->stop();
 
       // здесь обрабатываешь пакет, считаем что он весь пришел (или делаешь какие нить проверки)
       // ...
   }
}
 
YourClass::handleTimeout()
{
   // здесь считаем что весь пакет вовремя не пришел и что-то делаем.
}
 
 


Название: Re: QtSerialPort и определение конца пакета
Отправлено: titan83 от Ноябрь 12, 2013, 15:59
titan83, тебе надо использовать какой нибудь протокол обмена. Потому что как ты не контролируй время выборки из порта, ты всеравно не сможешь подстроится под реальный мир, и будут ситуации когда между опросами прийдет 0,5 пакета, или 1,23. Короче просто невозможно синхронизировать приемник и передатчик по времени.
Я использую протоколы обмена, целых две штуки: на одном порту у меня modbus rtu (т.е. двоичный), а на втором модуль zigbee, который шлет текстовые строки в строго определенном формате.
Полгодика назад я писал драйвер под uclinux для работы с modbus rtu, так там я свободно запускал таймер на 2 мс после каждого принятого байта и когда этот таймер срабатывал начинал обрабатывать пришедший кадр - все работало идеально, я был полностью синхронизирован и подстроен под внешний мир.
Но в Qt я не хочу падать так низко (уровень ядра), поэтому ищу как можно использовать имеющиеся инструменты.


Название: Re: QtSerialPort и определение конца пакета
Отправлено: titan83 от Ноябрь 12, 2013, 16:00
спасибо.
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?
может быть есть возможность допилить некое свойство аля endFrameTimeout (в миллисекундах), т.е. если после приема последнего байта прошло endFrameTimeout миллисекунд, то считаем, что все данные поступили и делаем emit?


Название: Re: QtSerialPort и определение конца пакета
Отправлено: Bepec от Ноябрь 12, 2013, 17:06
Обычно в таких программах на компьютере нельзя опознать окончание пакета по "временному промежутку". Потому нужно использовать протокол. Простейший используемый в примерах Qt - фиксированный байт(0xAA), далее размер пересылаемых данных(0x01) и далее данные(0x07).
Приходит колбаса аля
"AA 01 07 AA 02 01 01 AA 01 AA AA 02 0A AA AA 0A 01 02"
И согласно протоколу анализируем и получаем
"AA 01 07"        - один байт в пакете
"AA 02 01 01"    - два байта в пакете
"AA 01 AA"        - один байт, тут AA находится в данных
"AA 02 0A AA"    - и тут.
"AA 0A 01 02"     - а вот тут понимаем что пришла нам часть пакета. У пакета длина 10, пришло два. Значит в следущий раз должно прийти ещё 8 байтов.

PS Обмен у вас видно в шине медленный :)


Название: Re: QtSerialPort и определение конца пакета
Отправлено: kuzulis от Ноябрь 12, 2013, 21:09
Цитировать
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?

Сработало событие о приходе хотя бы одного байта в FIFO (например в винде это EV_RXCHAR), запускается автоматически чтение в readBuffer из FIFO всех байт по максимуму которые успели туда придти, высылается сигнал readyRead()... И так с начала.

Как то так :)

Цитировать
может быть есть возможность допилить некое свойство аля endFrameTimeout (в миллисекундах), т.е. если после приема последнего байта прошло endFrameTimeout миллисекунд, то считаем, что все данные поступили и делаем emit?

Невозможно в принципе обеспечить такую точность в user space.

UPD: Ты можешь попытаться самостоятельно установить SetCommTimeouts (http://msdn.microsoft.com/en-us/library/windows/desktop/aa363437%28v=vs.85%29.aspx), в винде или VMIN+VTIME в Unix если знаешь что с ними делать. Это должно обеспечить таймауты, но также вызовет и фризы. :)

Цитировать
на одном порту у меня modbus rtu (т.е. двоичный),

Тут проблем быть не должно т.к. ты всегда знаешь сколько байт должно придти, какой код функции ожидается и т.п.
Здесь можно просто поставить общий таймаут (как у в примере что я выше привел), и не париться.

Если важна производительность, то задержка может случиться только в случае приема битого пакета. В этом случае обычно просто уменьшают периодичность опроса данного узла (до совсем отключения) если постоянно от него идут ошибки.

Цитировать
а на втором модуль zigbee, который шлет текстовые строки в строго определенном формате

Тоже не понимаю, в чем тут проблема?  :)


Название: Re: QtSerialPort и определение конца пакета
Отправлено: titan83 от Ноябрь 13, 2013, 07:02
Обычно в таких программах на компьютере нельзя опознать окончание пакета по "временному промежутку". Потому нужно использовать протокол. Простейший используемый в примерах Qt - фиксированный байт(0xAA), далее размер пересылаемых данных(0x01) и далее данные(0x07).
Приходит колбаса аля
"AA 01 07 AA 02 01 01 AA 01 AA AA 02 0A AA AA 0A 01 02"
И согласно протоколу анализируем и получаем
"AA 01 07"        - один байт в пакете
"AA 02 01 01"    - два байта в пакете
"AA 01 AA"        - один байт, тут AA находится в данных
"AA 02 0A AA"    - и тут.
"AA 0A 01 02"     - а вот тут понимаем что пришла нам часть пакета. У пакета длина 10, пришло два. Значит в следущий раз должно прийти ещё 8 байтов.

PS Обмен у вас видно в шине медленный :)

К сожалению, протоколы уже есть готовые, т.к. я делаю прошивку только для одного устройства.


Название: Re: QtSerialPort и определение конца пакета
Отправлено: titan83 от Ноябрь 13, 2013, 07:05
Цитировать
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?

Сработало событие о приходе хотя бы одного байта в FIFO (например в винде это EV_RXCHAR), запускается автоматически чтение в readBuffer из FIFO всех байт по максимуму которые успели туда придти, высылается сигнал readyRead()... И так с начала.

Как то так :)

Цитировать
может быть есть возможность допилить некое свойство аля endFrameTimeout (в миллисекундах), т.е. если после приема последнего байта прошло endFrameTimeout миллисекунд, то считаем, что все данные поступили и делаем emit?

Невозможно в принципе обеспечить такую точность в user space.

UPD: Ты можешь попытаться самостоятельно установить SetCommTimeouts (http://msdn.microsoft.com/en-us/library/windows/desktop/aa363437%28v=vs.85%29.aspx), в винде или VMIN+VTIME в Unix если знаешь что с ними делать. Это должно обеспечить таймауты, но также вызовет и фризы. :)

Цитировать
на одном порту у меня modbus rtu (т.е. двоичный),

Тут проблем быть не должно т.к. ты всегда знаешь сколько байт должно придти, какой код функции ожидается и т.п.
Здесь можно просто поставить общий таймаут (как у в примере что я выше привел), и не париться.

Если важна производительность, то задержка может случиться только в случае приема битого пакета. В этом случае обычно просто уменьшают периодичность опроса данного узла (до совсем отключения) если постоянно от него идут ошибки.

Цитировать
а на втором модуль zigbee, который шлет текстовые строки в строго определенном формате

Тоже не понимаю, в чем тут проблема?  :)
Спасибо.
Услышал все, что меня интересовало, сейчас подумаю, как лучше все организовать.