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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: QtSerialPort и определение конца пакета (решено)  (Прочитано 8935 раз)
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 считает кадр завершенным?
« Последнее редактирование: Ноябрь 13, 2013, 07:44 от titan83 » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1 : Ноябрь 11, 2013, 20:51 »

Цитировать
Можно ли как-то контролировать время, после которого QtSerialPort считает кадр завершенным?

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

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

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

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

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

ArchLinux x86_64 / Win10 64 bit
titan83
Гость
« Ответ #2 : Ноябрь 12, 2013, 06:48 »

Т.е. по сигналу readyRead() я запускаю таймер, например, на 20 мс (у меня меньше 15 мс система не отрабатывает), а уже на его сигнал timeout() вешаю readAll() и всю свою логику?
Записан
alex312
Хакер
*****
Offline Offline

Сообщений: 606



Просмотр профиля
« Ответ #3 : Ноябрь 12, 2013, 10:35 »

titan83, тебе надо использовать какой нибудь протокол обмена. Потому что как ты не контролируй время выборки из порта, ты всеравно не сможешь подстроится под реальный мир, и будут ситуации когда между опросами прийдет 0,5 пакета, или 1,23. Короче просто невозможно синхронизировать приемник и передатчик по времени.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #4 : Ноябрь 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()
{
   // здесь считаем что весь пакет вовремя не пришел и что-то делаем.
}
 
 
Записан

ArchLinux x86_64 / Win10 64 bit
titan83
Гость
« Ответ #5 : Ноябрь 12, 2013, 15:59 »

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

спасибо.
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?
может быть есть возможность допилить некое свойство аля endFrameTimeout (в миллисекундах), т.е. если после приема последнего байта прошло endFrameTimeout миллисекунд, то считаем, что все данные поступили и делаем emit?
« Последнее редактирование: Ноябрь 12, 2013, 16:06 от titan83 » Записан
Bepec
Гость
« Ответ #7 : Ноябрь 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 Обмен у вас видно в шине медленный Улыбающийся
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #8 : Ноябрь 12, 2013, 21:09 »

Цитировать
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?

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

Как то так Улыбающийся

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

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

UPD: Ты можешь попытаться самостоятельно установить SetCommTimeouts, в винде или VMIN+VTIME в Unix если знаешь что с ними делать. Это должно обеспечить таймауты, но также вызовет и фризы. Улыбающийся

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

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

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

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

Тоже не понимаю, в чем тут проблема?  Улыбающийся
« Последнее редактирование: Ноябрь 12, 2013, 21:14 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
titan83
Гость
« Ответ #9 : Ноябрь 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 Обмен у вас видно в шине медленный Улыбающийся

К сожалению, протоколы уже есть готовые, т.к. я делаю прошивку только для одного устройства.
Записан
titan83
Гость
« Ответ #10 : Ноябрь 13, 2013, 07:05 »

Цитировать
но все-таки может пояснишь при каких условиях происходит emit(readyRead)?

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

Как то так Улыбающийся

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

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

UPD: Ты можешь попытаться самостоятельно установить SetCommTimeouts, в винде или VMIN+VTIME в Unix если знаешь что с ними делать. Это должно обеспечить таймауты, но также вызовет и фризы. Улыбающийся

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

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

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

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

Тоже не понимаю, в чем тут проблема?  Улыбающийся
Спасибо.
Услышал все, что меня интересовало, сейчас подумаю, как лучше все организовать.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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